next-lxd 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/LICENSE +21 -0
- package/README.md +780 -0
- package/bun.lock +317 -0
- package/classes/Certificate.ts +43 -0
- package/classes/Certificates.ts +34 -0
- package/classes/Client.ts +142 -0
- package/classes/Cluster.ts +174 -0
- package/classes/Image.ts +114 -0
- package/classes/Images.ts +38 -0
- package/classes/Instance.ts +599 -0
- package/classes/Instances.ts +44 -0
- package/classes/Network.ts +176 -0
- package/classes/Networks.ts +30 -0
- package/classes/Operation.ts +39 -0
- package/classes/Operations.ts +24 -0
- package/classes/Profile.ts +54 -0
- package/classes/Profiles.ts +30 -0
- package/classes/Project.ts +63 -0
- package/classes/Projects.ts +30 -0
- package/classes/Resource.ts +9 -0
- package/classes/StoragePool.ts +160 -0
- package/classes/StoragePools.ts +34 -0
- package/classes/Warnings.ts +39 -0
- package/index.ts +19 -0
- package/lxd-api-source.json +24022 -0
- package/package.json +36 -0
- package/tsconfig.json +21 -0
- package/types/index.d.ts +1087 -0
package/README.md
ADDED
|
@@ -0,0 +1,780 @@
|
|
|
1
|
+
# next-lxd
|
|
2
|
+
|
|
3
|
+
**Production-grade LXD client for Node.js** — wraps the LXD REST API (`/1.0`) with a typed, class-based interface, plus native SFTP support via `next-lxd-sftp`.
|
|
4
|
+
|
|
5
|
+
> **Status:** v0.1.0 — MIT
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install next-lxd
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Node.js 18+ (ES2022).
|
|
16
|
+
|
|
17
|
+
### Dependencies
|
|
18
|
+
|
|
19
|
+
| Package | Purpose |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `next-lxd-sftp` | Native Node-API addon for SFTP file operations |
|
|
22
|
+
| `undici` | HTTP client (used internally) |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { Client } from 'next-lxd';
|
|
30
|
+
|
|
31
|
+
const client = new Client({
|
|
32
|
+
url: 'https://10.0.0.1:8443',
|
|
33
|
+
cert: fs.readFileSync('client.crt'),
|
|
34
|
+
key: fs.readFileSync('client.key'),
|
|
35
|
+
allowInsecure: true,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Test the connection
|
|
39
|
+
const server = await client.connectionTest();
|
|
40
|
+
console.log(server.metadata.server_version);
|
|
41
|
+
|
|
42
|
+
// Access resource collections via client properties
|
|
43
|
+
const instances = client.instances;
|
|
44
|
+
const images = client.images;
|
|
45
|
+
const networks = client.networks;
|
|
46
|
+
const profiles = client.profiles;
|
|
47
|
+
const projects = client.projects;
|
|
48
|
+
const storagePools = client.storagePools;
|
|
49
|
+
const certificates = client.certificates;
|
|
50
|
+
const operations = client.operations;
|
|
51
|
+
const cluster = client.cluster;
|
|
52
|
+
const warnings = client.warnings;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Architecture
|
|
58
|
+
|
|
59
|
+
The package follows a two-level pattern:
|
|
60
|
+
|
|
61
|
+
- **Collection classes** (`Instances`, `Images`, `Networks`, etc.) — list resources, fetch individual resources by name, and create new resources (POST).
|
|
62
|
+
- **Resource classes** (`Instance`, `Image`, `Network`, etc.) — represent a single LXD resource and expose its CRUD operations and sub-resources.
|
|
63
|
+
|
|
64
|
+
All classes extend the abstract `Resource` base class, which holds a reference to the `Client`.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## API Reference
|
|
69
|
+
|
|
70
|
+
### `Client`
|
|
71
|
+
|
|
72
|
+
The entry point. Creates an HTTPS agent with mutual TLS authentication and exposes all LXD resource collections.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { Client, ClientOptions, RequestOptions, RawResponse } from 'next-lxd';
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Constructor
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
new Client(options: ClientOptions)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Option | Type | Description |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `url` | `string` | LXD server URL (e.g. `https://10.0.0.1:8443`) |
|
|
87
|
+
| `cert` | `string \| Buffer` | TLS client certificate |
|
|
88
|
+
| `key` | `string \| Buffer` | TLS client private key |
|
|
89
|
+
| `ca` | `string \| Buffer` (optional) | Custom CA certificate |
|
|
90
|
+
| `password` | `string` (optional) | Trust password (for `POST /1.0/certificates`) |
|
|
91
|
+
| `allowInsecure` | `boolean` (optional) | Skip TLS verification (`rejectUnauthorized: false`) |
|
|
92
|
+
|
|
93
|
+
#### Properties
|
|
94
|
+
|
|
95
|
+
| Property | Type | Description |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `url` | `URL` | Parsed server URL |
|
|
98
|
+
| `clientInfo` | `object` | Certificate/key metadata (read-only mirror) |
|
|
99
|
+
|
|
100
|
+
#### Methods
|
|
101
|
+
|
|
102
|
+
| Method | Description |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `request<T>(options)` | Performs an HTTPS request and returns the parsed JSON body of type `T` |
|
|
105
|
+
| `requestRaw<T>(options)` | Performs an HTTPS request and returns `RawResponse<T>` (status, headers, body) |
|
|
106
|
+
| `connectionTest()` | GET `/1.0` — returns `LxdServerResponse` with server info |
|
|
107
|
+
|
|
108
|
+
#### `request` / `requestRaw` Options
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
{
|
|
112
|
+
method?: string; // default 'GET'
|
|
113
|
+
path: string; // e.g. '/1.0/instances'
|
|
114
|
+
headers?: Record<string, any>;
|
|
115
|
+
body?: unknown; // serialized as JSON
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Collections (auto-instantiated)
|
|
120
|
+
|
|
121
|
+
Each collection is a public readonly property on the `Client` instance:
|
|
122
|
+
|
|
123
|
+
| Property | Class |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `client.instances` | `Instances` |
|
|
126
|
+
| `client.images` | `Images` |
|
|
127
|
+
| `client.networks` | `Networks` |
|
|
128
|
+
| `client.profiles` | `Profiles` |
|
|
129
|
+
| `client.projects` | `Projects` |
|
|
130
|
+
| `client.storagePools` | `StoragePools` |
|
|
131
|
+
| `client.certificates` | `Certificates` |
|
|
132
|
+
| `client.operations` | `Operations` |
|
|
133
|
+
| `client.cluster` | `Cluster` |
|
|
134
|
+
| `client.warnings` | `Warnings` |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### `Instances`
|
|
139
|
+
|
|
140
|
+
Collection class — access via `client.instances`.
|
|
141
|
+
|
|
142
|
+
| Method | HTTP | Path |
|
|
143
|
+
|---|---|---|
|
|
144
|
+
| `list(project?)` | GET | `/1.0/instances?project=...` |
|
|
145
|
+
| `get(name, project?)` | GET | `/1.0/instances/{name}` → returns `Instance` |
|
|
146
|
+
| `post(config, project?, target?)` | POST | `/1.0/instances` — creates an instance and returns the `Instance` object |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
### `Instance`
|
|
151
|
+
|
|
152
|
+
Represents a single instance. Returned by `Instances.get()` / `Instances.post()`.
|
|
153
|
+
|
|
154
|
+
#### Properties
|
|
155
|
+
|
|
156
|
+
| Property | Type |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `name` | `string` |
|
|
159
|
+
| `status` | `string` |
|
|
160
|
+
| `metadata` | `LxdInstanceResponse['metadata']` |
|
|
161
|
+
| `project` | `string \| undefined` |
|
|
162
|
+
|
|
163
|
+
#### Methods (direct CRUD)
|
|
164
|
+
|
|
165
|
+
| Method | HTTP | Path |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| `delete()` | DELETE | `/1.0/instances/{name}` |
|
|
168
|
+
| `put(data, project?)` | PUT | `/1.0/instances/{name}` — full replace |
|
|
169
|
+
| `patch(data, project?)` | PATCH | `/1.0/instances/{name}` — partial update |
|
|
170
|
+
| `post(data, project?)` | POST | `/1.0/instances/{name}` — rename/migrate |
|
|
171
|
+
| `rebuild(data, project?)` | POST | `/1.0/instances/{name}/rebuild` |
|
|
172
|
+
|
|
173
|
+
#### Nested sub-resources
|
|
174
|
+
|
|
175
|
+
##### `instance.console`
|
|
176
|
+
|
|
177
|
+
| Method | HTTP | Path |
|
|
178
|
+
|---|---|---|
|
|
179
|
+
| `get()` | GET | `/1.0/instances/{name}/console` |
|
|
180
|
+
| `post({height, width, type})` | POST | `/1.0/instances/{name}/console` |
|
|
181
|
+
|
|
182
|
+
##### `instance.exec(data)` — single method
|
|
183
|
+
|
|
184
|
+
| Method | HTTP | Path |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| `exec(data)` | POST | `/1.0/instances/{name}/exec` |
|
|
187
|
+
|
|
188
|
+
See `LxdInstanceExecRequest` for the request shape (command, environment, interactive, etc.).
|
|
189
|
+
|
|
190
|
+
##### `instance.logs`
|
|
191
|
+
|
|
192
|
+
| Method | HTTP | Path |
|
|
193
|
+
|---|---|---|
|
|
194
|
+
| `get()` | GET | `/1.0/instances/{name}/logs` — list log files |
|
|
195
|
+
| `file(filename).get()` | GET | `/1.0/instances/{name}/logs/{filename}` |
|
|
196
|
+
| `file(filename).delete()` | DELETE | `/1.0/instances/{name}/logs/{filename}` |
|
|
197
|
+
| `execOutput.get()` | GET | `/1.0/instances/{name}/logs/exec-output` |
|
|
198
|
+
|
|
199
|
+
##### `instance.state`
|
|
200
|
+
|
|
201
|
+
| Method | HTTP | Path |
|
|
202
|
+
|---|---|---|
|
|
203
|
+
| `get(project?)` | GET | `/1.0/instances/{name}/state` — CPU, memory, network, processes |
|
|
204
|
+
| `put(data, project?)` | PUT | `/1.0/instances/{name}/state` — start/stop/restart/freeze |
|
|
205
|
+
|
|
206
|
+
##### `instance.files`
|
|
207
|
+
|
|
208
|
+
| Method | HTTP | Path | Notes |
|
|
209
|
+
|---|---|---|---|
|
|
210
|
+
| `file(path).get()` | GET | `/1.0/instances/{name}/files?path=...` | Returns headers + body |
|
|
211
|
+
| `file(path).delete()` | DELETE | `/1.0/instances/{name}/files?path=...` | |
|
|
212
|
+
| `file(path).head()` | HEAD | `/1.0/instances/{name}/files?path=...` | Returns headers only |
|
|
213
|
+
| `file(path).post(body, headers?)` | POST | `/1.0/instances/{name}/files?path=...` | Upload. Headers: `X-LXD-uid`, `X-LXD-gid`, `X-LXD-mode`, `X-LXD-type`, `X-LXD-write` |
|
|
214
|
+
|
|
215
|
+
##### `instance.snapshots`
|
|
216
|
+
|
|
217
|
+
| Method | HTTP | Path |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
| `list(project?)` | GET | `/1.0/instances/{name}/snapshots` |
|
|
220
|
+
| `get(snapshotName, project?)` | GET | `/1.0/instances/{name}/snapshots/{snapshot}` |
|
|
221
|
+
| `post(data, project?)` | POST | `/1.0/instances/{name}/snapshots` — create snapshot |
|
|
222
|
+
| `snapshot(name).put(data, project?)` | PUT | full update |
|
|
223
|
+
| `snapshot(name).patch(data, project?)` | PATCH | partial update |
|
|
224
|
+
| `snapshot(name).delete(project?)` | DELETE | |
|
|
225
|
+
| `snapshot(name).post(data, project?)` | POST | rename/restore |
|
|
226
|
+
|
|
227
|
+
##### `instance.backups`
|
|
228
|
+
|
|
229
|
+
| Method | HTTP | Path |
|
|
230
|
+
|---|---|---|
|
|
231
|
+
| `list(project?)` | GET | `/1.0/instances/{name}/backups` |
|
|
232
|
+
| `get(backupName, project?)` | GET | `/1.0/instances/{name}/backups/{backup}` |
|
|
233
|
+
| `post(data, project?)` | POST | `/1.0/instances/{name}/backups` — create backup |
|
|
234
|
+
| `backup(name).delete(project?)` | DELETE | |
|
|
235
|
+
| `backup(name).post(data, project?)` | POST | rename |
|
|
236
|
+
| `backup(name).export(project?)` | GET raw | download backup as raw response |
|
|
237
|
+
|
|
238
|
+
##### `instance.metaData`
|
|
239
|
+
|
|
240
|
+
| Method | HTTP | Path |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| `get()` | GET | `/1.0/instances/{name}/metadata` |
|
|
243
|
+
| `put(data, project?)` | PUT | |
|
|
244
|
+
| `patch(data, project?)` | PATCH | |
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
#### SFTP (native addon)
|
|
249
|
+
|
|
250
|
+
The `instance.sftp` namespace provides secure file transfer directly into the instance's filesystem via the `next-lxd-sftp` native addon.
|
|
251
|
+
|
|
252
|
+
> **Note:** The native addon (`next-lxd-sftp`) must be installed and built for your platform.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// 1. Connect — returns a sessionId
|
|
256
|
+
const { sessionId } = await instance.sftp.connect();
|
|
257
|
+
|
|
258
|
+
// 2. Use the session
|
|
259
|
+
const session = instance.sftp.session(sessionId);
|
|
260
|
+
|
|
261
|
+
// Directory operations
|
|
262
|
+
const entries = await session.readDir('/root');
|
|
263
|
+
await session.mkdir('/root/mydir');
|
|
264
|
+
await session.mkdirAll('/root/a/b/c');
|
|
265
|
+
await session.removeDir('/root/olddir');
|
|
266
|
+
|
|
267
|
+
// File operations
|
|
268
|
+
const file = await session.open('/root/file.txt'); // or openFile / create
|
|
269
|
+
await file.write(Buffer.from('hello'));
|
|
270
|
+
const data: Buffer = await file.read(1024);
|
|
271
|
+
await file.close();
|
|
272
|
+
|
|
273
|
+
// Stat / metadata
|
|
274
|
+
const stat = await session.stat('/root/file.txt');
|
|
275
|
+
await session.chmod('/root/file.txt', 0o755);
|
|
276
|
+
await session.chown('/root/file.txt', 1000, 1000);
|
|
277
|
+
await session.chtimes('/root/file.txt', new Date(), new Date());
|
|
278
|
+
|
|
279
|
+
// Path utilities
|
|
280
|
+
const real = await session.realPath('/root/../root');
|
|
281
|
+
const link = await session.readLink('/root/symlink');
|
|
282
|
+
const wd = await session.getWd();
|
|
283
|
+
const matches = await session.glob('*.txt');
|
|
284
|
+
|
|
285
|
+
// Remove / rename
|
|
286
|
+
await session.remove('/root/trash.txt');
|
|
287
|
+
await session.rename('/root/a.txt', '/root/b.txt');
|
|
288
|
+
await session.posixRename('/root/a.txt', '/root/b.txt');
|
|
289
|
+
|
|
290
|
+
// 3. Disconnect when done
|
|
291
|
+
await session.disconnect();
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Available session methods:**
|
|
295
|
+
|
|
296
|
+
| Method | Description |
|
|
297
|
+
|---|---|
|
|
298
|
+
| `disconnect()` | Close the SFTP session |
|
|
299
|
+
| `readDir(path)` | List directory entries |
|
|
300
|
+
| `stat(path)` | Get file/dir attributes |
|
|
301
|
+
| `lStat(path)` | Get attributes (no follow symlink) |
|
|
302
|
+
| `readLink(path)` | Read symlink target |
|
|
303
|
+
| `realPath(path)` | Resolve to absolute path |
|
|
304
|
+
| `getWd()` | Get working directory |
|
|
305
|
+
| `glob(pattern)` | Glob pattern matching |
|
|
306
|
+
| `open(path)` | Open file for read/write |
|
|
307
|
+
| `openFile(path, flags?)` | Open file with flags |
|
|
308
|
+
| `create(path)` | Create new file |
|
|
309
|
+
| `closeFile(fileId)` | Close file handle |
|
|
310
|
+
| `remove(path)` | Delete file |
|
|
311
|
+
| `removeDir(path)` | Delete empty directory |
|
|
312
|
+
| `rename(oldPath, newPath)` | Rename file/dir |
|
|
313
|
+
| `posixRename(oldPath, newPath)` | POSIX rename (overwrite if exists) |
|
|
314
|
+
| `mkdir(path)` | Create directory |
|
|
315
|
+
| `mkdirAll(path)` | Create directory (parents too) |
|
|
316
|
+
| `chmod(path, mode)` | Change permissions |
|
|
317
|
+
| `chown(path, uid, gid)` | Change owner/group |
|
|
318
|
+
| `chtimes(path, atime, mtime)` | Change access/modify times |
|
|
319
|
+
|
|
320
|
+
**File handle methods** (returned by `open`, `openFile`, `create`):
|
|
321
|
+
|
|
322
|
+
| Method | Description |
|
|
323
|
+
|---|---|
|
|
324
|
+
| `read(length?)` | Read up to `length` bytes → `Buffer` |
|
|
325
|
+
| `write(data)` | Write `Buffer`, `Uint8Array`, or `string` |
|
|
326
|
+
| `close()` | Close the file handle |
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
### `Images`
|
|
331
|
+
|
|
332
|
+
Collection class — access via `client.images`.
|
|
333
|
+
|
|
334
|
+
| Method | HTTP | Path |
|
|
335
|
+
|---|---|---|
|
|
336
|
+
| `list(project?)` | GET | `/1.0/images?project=...` |
|
|
337
|
+
| `get(fingerprint, project?)` | GET | `/1.0/images/{fingerprint}` → returns `Image` |
|
|
338
|
+
| `post(config, project?)` | POST | `/1.0/images` — create (returns `LxdBaseResponse`) |
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
### `Image`
|
|
343
|
+
|
|
344
|
+
Represents a single image. Returned by `Images.get()`.
|
|
345
|
+
|
|
346
|
+
#### Properties
|
|
347
|
+
|
|
348
|
+
| Property | Type |
|
|
349
|
+
|---|---|
|
|
350
|
+
| `name` | `string` (fingerprint) |
|
|
351
|
+
| `metadata` | `LxdImageResponse['metadata']` |
|
|
352
|
+
|
|
353
|
+
#### Methods
|
|
354
|
+
|
|
355
|
+
| Method | HTTP | Path |
|
|
356
|
+
|---|---|---|
|
|
357
|
+
| `delete()` | DELETE | `/1.0/images/{fingerprint}` |
|
|
358
|
+
| `put(data)` | PUT | `/1.0/images/{fingerprint}` — full replace |
|
|
359
|
+
| `patch(data)` | PATCH | `/1.0/images/{fingerprint}` — partial update |
|
|
360
|
+
| `refresh()` | POST | `/1.0/images/{fingerprint}/refresh` |
|
|
361
|
+
| `export()` | GET raw | `/1.0/images/{fingerprint}/export` — download as raw response |
|
|
362
|
+
| `secret()` | POST | `/1.0/images/{fingerprint}/secret` — returns a secret for public download |
|
|
363
|
+
|
|
364
|
+
#### `image.aliases`
|
|
365
|
+
|
|
366
|
+
| Method | HTTP | Path |
|
|
367
|
+
|---|---|---|
|
|
368
|
+
| `list()` | GET | `/1.0/images/{fingerprint}/aliases` |
|
|
369
|
+
| `create(data)` | POST | `/1.0/images/{fingerprint}/aliases` |
|
|
370
|
+
| `entry(name).get()` | GET | `/1.0/images/aliases/{name}` |
|
|
371
|
+
| `entry(name).put(data)` | PUT | |
|
|
372
|
+
| `entry(name).patch(data)` | PATCH | |
|
|
373
|
+
| `entry(name).delete()` | DELETE | |
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
### `Networks`
|
|
378
|
+
|
|
379
|
+
Collection class — access via `client.networks`.
|
|
380
|
+
|
|
381
|
+
| Method | HTTP | Path |
|
|
382
|
+
|---|---|---|
|
|
383
|
+
| `list(project?)` | GET | `/1.0/networks?project=...` |
|
|
384
|
+
| `get(name, project?)` | GET | `/1.0/networks/{name}` → returns `Network` |
|
|
385
|
+
| `post(config, project?)` | POST | `/1.0/networks` — create and return `Network` |
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
### `Network`
|
|
390
|
+
|
|
391
|
+
Represents a single network. Returned by `Networks.get()` / `Networks.post()`.
|
|
392
|
+
|
|
393
|
+
#### Properties
|
|
394
|
+
|
|
395
|
+
| Property | Type |
|
|
396
|
+
|---|---|
|
|
397
|
+
| `name` | `string` |
|
|
398
|
+
| `type` | `string` |
|
|
399
|
+
| `managed` | `boolean` |
|
|
400
|
+
| `metadata` | `LxdNetworkResponse['metadata']` |
|
|
401
|
+
|
|
402
|
+
#### Methods
|
|
403
|
+
|
|
404
|
+
| Method | HTTP | Path |
|
|
405
|
+
|---|---|---|
|
|
406
|
+
| `delete(project?)` | DELETE | `/1.0/networks/{name}` |
|
|
407
|
+
| `put(data, project?)` | PUT | full replace |
|
|
408
|
+
| `patch(data, project?)` | PATCH | partial update |
|
|
409
|
+
| `state(project?)` | GET | `/1.0/networks/{name}/state` |
|
|
410
|
+
| `leases(project?)` | GET | `/1.0/networks/{name}/leases` |
|
|
411
|
+
|
|
412
|
+
#### Sub-resources
|
|
413
|
+
|
|
414
|
+
**`network.forwards`** — network address forwards:
|
|
415
|
+
|
|
416
|
+
`list()`, `get(listenAddress)`, `post(data)`, `put(listenAddress, data)`, `delete(listenAddress)`
|
|
417
|
+
|
|
418
|
+
**`network.acls`** — network ACLs:
|
|
419
|
+
|
|
420
|
+
`list()`, `get(aclName)`, `post(data)`, `put(aclName, data)`, `delete(aclName)`
|
|
421
|
+
|
|
422
|
+
**`network.peers`** — network peers:
|
|
423
|
+
|
|
424
|
+
`list()`, `get(peerName)`, `put(peerName, data)`, `delete(peerName)`
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
### `Profiles`
|
|
429
|
+
|
|
430
|
+
Collection class — access via `client.profiles`.
|
|
431
|
+
|
|
432
|
+
| Method | HTTP | Path |
|
|
433
|
+
|---|---|---|
|
|
434
|
+
| `list(project?)` | GET | `/1.0/profiles?project=...` |
|
|
435
|
+
| `get(name, project?)` | GET | `/1.0/profiles/{name}` → returns `Profile` |
|
|
436
|
+
| `post(config, project?)` | POST | `/1.0/profiles` — create and return `Profile` |
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
### `Profile`
|
|
441
|
+
|
|
442
|
+
Represents a single profile. Returned by `Profiles.get()` / `Profiles.post()`.
|
|
443
|
+
|
|
444
|
+
#### Properties
|
|
445
|
+
|
|
446
|
+
| Property | Type |
|
|
447
|
+
|---|---|
|
|
448
|
+
| `name` | `string` |
|
|
449
|
+
| `description` | `string` |
|
|
450
|
+
| `metadata` | `LxdProfileResponse['metadata']` |
|
|
451
|
+
|
|
452
|
+
#### Methods
|
|
453
|
+
|
|
454
|
+
| Method | HTTP | Path |
|
|
455
|
+
|---|---|---|
|
|
456
|
+
| `delete(project?)` | DELETE | `/1.0/profiles/{name}` |
|
|
457
|
+
| `put(data, project?)` | PUT | full replace |
|
|
458
|
+
| `patch(data, project?)` | PATCH | partial update |
|
|
459
|
+
| `post(data, project?)` | POST | rename |
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
### `Projects`
|
|
464
|
+
|
|
465
|
+
Collection class — access via `client.projects`.
|
|
466
|
+
|
|
467
|
+
| Method | HTTP | Path |
|
|
468
|
+
|---|---|---|
|
|
469
|
+
| `list(project?)` | GET | `/1.0/projects?project=...` |
|
|
470
|
+
| `get(name, project?)` | GET | `/1.0/projects/{name}` → returns `Project` |
|
|
471
|
+
| `post(config, project?)` | POST | `/1.0/projects` — create and return `Project` |
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
### `Project`
|
|
476
|
+
|
|
477
|
+
Represents a single project. Returned by `Projects.get()` / `Projects.post()`.
|
|
478
|
+
|
|
479
|
+
#### Properties
|
|
480
|
+
|
|
481
|
+
| Property | Type |
|
|
482
|
+
|---|---|
|
|
483
|
+
| `name` | `string` |
|
|
484
|
+
| `description` | `string` |
|
|
485
|
+
| `metadata` | `LxdProjectResponse['metadata']` |
|
|
486
|
+
|
|
487
|
+
#### Methods
|
|
488
|
+
|
|
489
|
+
| Method | HTTP | Path |
|
|
490
|
+
|---|---|---|
|
|
491
|
+
| `delete(force?)` | DELETE | `/1.0/projects/{name}?force=1` |
|
|
492
|
+
| `put(data)` | PUT | full replace |
|
|
493
|
+
| `patch(data)` | PATCH | partial update |
|
|
494
|
+
| `post(data)` | POST | rename |
|
|
495
|
+
| `state()` | GET | `/1.0/projects/{name}/state` — resource usage |
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
### `StoragePools`
|
|
500
|
+
|
|
501
|
+
Collection class — access via `client.storagePools`.
|
|
502
|
+
|
|
503
|
+
| Method | HTTP | Path |
|
|
504
|
+
|---|---|---|
|
|
505
|
+
| `list(project?)` | GET | `/1.0/storage-pools?project=...` |
|
|
506
|
+
| `get(name, project?)` | GET | `/1.0/storage-pools/{name}` → returns `StoragePool` |
|
|
507
|
+
| `post(config, project?)` | POST | `/1.0/storage-pools` — create and return `StoragePool` |
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
### `StoragePool`
|
|
512
|
+
|
|
513
|
+
Represents a single storage pool. Returned by `StoragePools.get()` / `StoragePools.post()`.
|
|
514
|
+
|
|
515
|
+
#### Properties
|
|
516
|
+
|
|
517
|
+
| Property | Type |
|
|
518
|
+
|---|---|
|
|
519
|
+
| `name` | `string` |
|
|
520
|
+
| `driver` | `string` |
|
|
521
|
+
| `metadata` | `LxdStoragePoolResponse['metadata']` |
|
|
522
|
+
|
|
523
|
+
#### Methods
|
|
524
|
+
|
|
525
|
+
| Method | HTTP | Path |
|
|
526
|
+
|---|---|---|
|
|
527
|
+
| `delete()` | DELETE | `/1.0/storage-pools/{name}` |
|
|
528
|
+
| `put(data)` | PUT | full replace |
|
|
529
|
+
| `patch(data)` | PATCH | partial update |
|
|
530
|
+
| `resources()` | GET | `/1.0/storage-pools/{name}/resources` |
|
|
531
|
+
|
|
532
|
+
#### Sub-resources: `storagePool.volumes`
|
|
533
|
+
|
|
534
|
+
| Method | HTTP | Path |
|
|
535
|
+
|---|---|---|
|
|
536
|
+
| `list(project?)` | GET | `/1.0/storage-pools/{name}/volumes` |
|
|
537
|
+
| `get(type, name, project?)` | GET | `/1.0/storage-pools/{name}/volumes/{type}/{name}` |
|
|
538
|
+
| `post(type, data, project?)` | POST | create volume |
|
|
539
|
+
| `put(type, name, data, project?)` | PUT | update volume |
|
|
540
|
+
| `patch(type, name, data, project?)` | PATCH | partial update volume |
|
|
541
|
+
| `delete(type, name, project?)` | DELETE | |
|
|
542
|
+
| `rename(type, name, data, project?)` | POST | rename volume |
|
|
543
|
+
|
|
544
|
+
**`storagePool.volumes.snapshots`** — volume snapshot operations:
|
|
545
|
+
|
|
546
|
+
`list(type, volumeName)`, `get(type, volumeName, snapshotName)`, `post(type, volumeName, data)`, `put(type, volumeName, snapshotName, data)`, `delete(type, volumeName, snapshotName)`, `rename(type, volumeName, snapshotName, data)`
|
|
547
|
+
|
|
548
|
+
---
|
|
549
|
+
|
|
550
|
+
### `Certificates`
|
|
551
|
+
|
|
552
|
+
Collection class — access via `client.certificates`.
|
|
553
|
+
|
|
554
|
+
| Method | HTTP | Path |
|
|
555
|
+
|---|---|---|
|
|
556
|
+
| `list()` | GET | `/1.0/certificates` |
|
|
557
|
+
| `get(fingerprint)` | GET | `/1.0/certificates/{fingerprint}` → returns `Certificate` |
|
|
558
|
+
| `post(config)` | POST | `/1.0/certificates` — add certificate |
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
### `Certificate`
|
|
563
|
+
|
|
564
|
+
Represents a single certificate. Returned by `Certificates.get()`.
|
|
565
|
+
|
|
566
|
+
| Property | Type |
|
|
567
|
+
|---|---|
|
|
568
|
+
| `fingerprint` | `string` |
|
|
569
|
+
| `metadata` | `LxdCertificateResponse['metadata']` |
|
|
570
|
+
|
|
571
|
+
#### Methods
|
|
572
|
+
|
|
573
|
+
| Method | HTTP | Path |
|
|
574
|
+
|---|---|---|
|
|
575
|
+
| `delete()` | DELETE | `/1.0/certificates/{fingerprint}` |
|
|
576
|
+
| `put(data)` | PUT | full replace |
|
|
577
|
+
| `patch(data)` | PATCH | partial update |
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
### `Operations`
|
|
582
|
+
|
|
583
|
+
Collection class — access via `client.operations`.
|
|
584
|
+
|
|
585
|
+
| Method | HTTP | Path |
|
|
586
|
+
|---|---|---|
|
|
587
|
+
| `list(project?)` | GET | `/1.0/operations?project=...` |
|
|
588
|
+
| `get(id)` | GET | `/1.0/operations/{id}` → returns `Operation` |
|
|
589
|
+
|
|
590
|
+
---
|
|
591
|
+
|
|
592
|
+
### `Operation`
|
|
593
|
+
|
|
594
|
+
Represents a single operation. Returned by `Operations.get()`.
|
|
595
|
+
|
|
596
|
+
| Property | Type |
|
|
597
|
+
|---|---|
|
|
598
|
+
| `id` | `string` |
|
|
599
|
+
| `metadata` | `LxdOperationResponse['metadata']` |
|
|
600
|
+
|
|
601
|
+
#### Methods
|
|
602
|
+
|
|
603
|
+
| Method | HTTP | Path |
|
|
604
|
+
|---|---|---|
|
|
605
|
+
| `wait(timeout?)` | GET | `/1.0/operations/{id}/wait?timeout=...` — blocks until complete |
|
|
606
|
+
| `cancel()` | DELETE | `/1.0/operations/{id}` |
|
|
607
|
+
| `websocketUrl(secret?)` | — | Returns the WebSocket URL for this operation (optionally with `?secret=...`) |
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
### `Cluster`
|
|
612
|
+
|
|
613
|
+
Collection class — access via `client.cluster`.
|
|
614
|
+
|
|
615
|
+
| Method | HTTP | Path |
|
|
616
|
+
|---|---|---|
|
|
617
|
+
| `get()` | GET | `/1.0/cluster` |
|
|
618
|
+
| `put(data)` | PUT | `/1.0/cluster` — bootstrap/join |
|
|
619
|
+
| `updateCertificate(data)` | PUT | `/1.0/cluster/certificate` |
|
|
620
|
+
|
|
621
|
+
#### Sub-resources
|
|
622
|
+
|
|
623
|
+
**`cluster.members`:**
|
|
624
|
+
|
|
625
|
+
`list()`, `get(name)`, `put(name, data)`, `patch(name, data)`, `rename(name, data)`, `delete(name)`, `state(name)`, `evacuate(name, data?)`, `restore(name, data?)`, `joinToken(data)`
|
|
626
|
+
|
|
627
|
+
**`cluster.groups`:**
|
|
628
|
+
|
|
629
|
+
`list()`, `get(name)`, `post(data)`, `put(name, data)`, `patch(name, data)`, `rename(name, data)`, `delete(name)`
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
### `Warnings`
|
|
634
|
+
|
|
635
|
+
Collection class — access via `client.warnings`.
|
|
636
|
+
|
|
637
|
+
| Method | HTTP | Path |
|
|
638
|
+
|---|---|---|
|
|
639
|
+
| `list(project?)` | GET | `/1.0/warnings?project=...` |
|
|
640
|
+
| `get(uuid)` | GET | `/1.0/warnings/{uuid}` |
|
|
641
|
+
| `put(uuid, data)` | PUT | `/1.0/warnings/{uuid}` |
|
|
642
|
+
| `delete(uuid)` | DELETE | `/1.0/warnings/{uuid}` |
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
## Type Exports
|
|
647
|
+
|
|
648
|
+
Key types (import from `next-lxd`):
|
|
649
|
+
|
|
650
|
+
```typescript
|
|
651
|
+
// Base response wrapper
|
|
652
|
+
LxdBaseResponse<T>
|
|
653
|
+
|
|
654
|
+
// Server
|
|
655
|
+
LxdServerResponse, LxdServerMetadata, LxdEnvironment
|
|
656
|
+
|
|
657
|
+
// Instances
|
|
658
|
+
InstanceCreateRequest, LxdInstanceResponse, LxdInstanceStateResponse,
|
|
659
|
+
LxdInstanceStatePut, LxdInstanceExecRequest, LxdInstanceExecResponse,
|
|
660
|
+
LxdInstanceConsoleResponse, LxdInstancePost, LxdInstancePutRequest,
|
|
661
|
+
LxdInstanceRebuildPost, LxdInstanceDeleteResponse
|
|
662
|
+
|
|
663
|
+
// Instance snapshots
|
|
664
|
+
LxdInstanceSnapshotsResponse, LxdInstanceSnapshotResponse,
|
|
665
|
+
LxdInstanceSnapshotsPost, LxdInstanceSnapshotPut, LxdInstanceSnapshotPost
|
|
666
|
+
|
|
667
|
+
// Instance backups
|
|
668
|
+
LxdInstanceBackupsResponse, LxdInstanceBackupResponse,
|
|
669
|
+
LxdInstanceBackupsPost, LxdInstanceBackupPost
|
|
670
|
+
|
|
671
|
+
// Images
|
|
672
|
+
LxdImageResponse, LxdImagesResponse, LxdImagePut, LxdImagesPost,
|
|
673
|
+
LxdImageAliasesResponse, LxdImageAliasResponse,
|
|
674
|
+
LxdImageAliasesPost, LxdImageAliasesEntryPut
|
|
675
|
+
|
|
676
|
+
// Networks
|
|
677
|
+
LxdNetworkResponse, LxdNetworksResponse, LxdNetworkPut, LxdNetworksPost,
|
|
678
|
+
LxdNetworkLeasesResponse, LxdNetworkForwardsResponse, LxdNetworkForwardResponse,
|
|
679
|
+
LxdNetworkForwardsPost, LxdNetworkForwardPut,
|
|
680
|
+
LxdNetworkACLsResponse, LxdNetworkACLResponse, LxdNetworkACLsPost, LxdNetworkACLPut,
|
|
681
|
+
LxdNetworkPeersResponse, LxdNetworkPeerResponse, LxdNetworkPeerPut
|
|
682
|
+
|
|
683
|
+
// Profiles
|
|
684
|
+
LxdProfileResponse, LxdProfilesResponse, LxdProfilePut, LxdProfilesPost, LxdProfilePost
|
|
685
|
+
|
|
686
|
+
// Projects
|
|
687
|
+
LxdProjectResponse, LxdProjectsResponse, LxdProjectPut, LxdProjectsPost, LxdProjectPost,
|
|
688
|
+
LxdProjectStateResponse
|
|
689
|
+
|
|
690
|
+
// Storage pools
|
|
691
|
+
LxdStoragePoolResponse, LxdStoragePoolsResponse, LxdStoragePoolPut, LxdStoragePoolsPost,
|
|
692
|
+
LxdStorageVolumesResponse, LxdStorageVolumeResponse, LxdStorageVolumesPost,
|
|
693
|
+
LxdStorageVolumePut, LxdStorageVolumePost,
|
|
694
|
+
LxdStorageVolumeSnapshotsResponse, LxdStorageVolumeSnapshotResponse,
|
|
695
|
+
LxdStorageVolumeSnapshotsPost, LxdStorageVolumeSnapshotPut, LxdStorageVolumeSnapshotPost
|
|
696
|
+
|
|
697
|
+
// Certificates
|
|
698
|
+
LxdCertificateResponse, LxdCertificatesResponse, LxdCertificatePut, LxdCertificatesPost
|
|
699
|
+
|
|
700
|
+
// Operations
|
|
701
|
+
LxdOperationResponse, LxdOperationsResponse
|
|
702
|
+
|
|
703
|
+
// Cluster
|
|
704
|
+
LxdClusterResponse, LxdClusterPut, LxdClusterCertificatePut,
|
|
705
|
+
LxdClusterMembersResponse, LxdClusterMemberResponse,
|
|
706
|
+
LxdClusterMemberPut, LxdClusterMemberPost, LxdClusterMembersPost,
|
|
707
|
+
LxdClusterMemberStateResponse, LxdClusterMemberStatePost, LxdClusterMemberJoinToken,
|
|
708
|
+
LxdClusterGroupsResponse, LxdClusterGroupResponse,
|
|
709
|
+
LxdClusterGroupPut, LxdClusterGroupPost, LxdClusterGroupsPost
|
|
710
|
+
|
|
711
|
+
// Warnings
|
|
712
|
+
LxdWarningsResponse, LxdWarningResponse, LxdWarningPut
|
|
713
|
+
|
|
714
|
+
// Events
|
|
715
|
+
LxdEvent
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
## Examples
|
|
721
|
+
|
|
722
|
+
### List all instances with their state
|
|
723
|
+
|
|
724
|
+
```typescript
|
|
725
|
+
const client = new Client({ url, cert, key });
|
|
726
|
+
const instances = await client.instances.list();
|
|
727
|
+
|
|
728
|
+
for (const name of instances.metadata) {
|
|
729
|
+
const instance = await client.instances.get(name);
|
|
730
|
+
console.log(`${name}: ${instance.status}`);
|
|
731
|
+
}
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
### Create and start a container
|
|
735
|
+
|
|
736
|
+
```typescript
|
|
737
|
+
const instance = await client.instances.post({
|
|
738
|
+
name: 'my-container',
|
|
739
|
+
source: {
|
|
740
|
+
type: 'image',
|
|
741
|
+
alias: 'ubuntu/22.04'
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
// Start it
|
|
746
|
+
await instance.state.put({ action: 'start' });
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
### Upload a file via SFTP
|
|
750
|
+
|
|
751
|
+
```typescript
|
|
752
|
+
const instance = await client.instances.get('my-container');
|
|
753
|
+
|
|
754
|
+
// Wait for it to be running
|
|
755
|
+
await instance.state.put({ action: 'start' });
|
|
756
|
+
|
|
757
|
+
// SFTP
|
|
758
|
+
const { sessionId } = await instance.sftp.connect();
|
|
759
|
+
const session = instance.sftp.session(sessionId);
|
|
760
|
+
|
|
761
|
+
const file = await session.create('/root/hello.txt');
|
|
762
|
+
await file.write(Buffer.from('Hello, LXD!'));
|
|
763
|
+
await file.close();
|
|
764
|
+
await session.disconnect();
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
769
|
+
## Development
|
|
770
|
+
|
|
771
|
+
```bash
|
|
772
|
+
# Install dependencies
|
|
773
|
+
bun install # or npm install
|
|
774
|
+
|
|
775
|
+
# Build TypeScript
|
|
776
|
+
npm run build
|
|
777
|
+
|
|
778
|
+
# Clean build output
|
|
779
|
+
npm run clean
|
|
780
|
+
```
|