@sourceregistry/node-ovsdb 1.0.2 → 1.0.3
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 +281 -75
- package/bin/ovsdb-generate.cjs +7 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/generator.d.ts +136 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +382 -47
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es +613 -0
- package/dist/index.es.map +1 -0
- package/dist/types/index.d.ts +417 -137
- package/dist/types/index.d.ts.map +1 -0
- package/dist-cli/cli.js +167 -0
- package/dist-cli/generator.js +216 -0
- package/dist-cli/index.js +742 -0
- package/dist-cli/types/index.js +2 -0
- package/package.json +31 -13
- package/dist/index.cjs.js +0 -4
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.es.js +0 -221
- package/dist/index.es.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,124 +1,330 @@
|
|
|
1
1
|
# @sourceregistry/node-ovsdb
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
4
|
+
[](https://www.npmjs.com/package/@sourceregistry/node-ovsdb)
|
|
5
|
+
[](https://codecov.io/gh/SourceRegistry/node-ovsdb)
|
|
4
6
|
[](https://github.com/SourceRegistry/node-ovsdb/actions/workflows/publish-npm.yml)
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
Low-level OVSDB client for Node.js with:
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
- Unix socket, TCP, and TLS transports
|
|
11
|
+
- RFC 7047 core RPC support
|
|
12
|
+
- Open vSwitch monitor extensions
|
|
13
|
+
- typed transaction and monitor payloads
|
|
14
|
+
- event-driven notifications
|
|
15
|
+
- schema-to-TypeScript generation CLI
|
|
16
|
+
- TSDoc-ready public API for Typedoc
|
|
9
17
|
|
|
10
18
|
## Installation
|
|
11
19
|
|
|
12
|
-
Install the package using npm:
|
|
13
|
-
|
|
14
20
|
```bash
|
|
15
21
|
npm install @sourceregistry/node-ovsdb
|
|
16
22
|
```
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
Generate types from a schema with:
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
```bash
|
|
27
|
+
npx ovsdb-generate --help
|
|
28
|
+
```
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
## What It Supports
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
import { OVSDBClient } from '@sourceregistry/node-ovsdb';
|
|
32
|
+
The client is intentionally low-level and maps closely to the wire protocol.
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
- `list_dbs`
|
|
35
|
+
- `get_schema`
|
|
36
|
+
- `transact`
|
|
37
|
+
- `cancel`
|
|
38
|
+
- `monitor`
|
|
39
|
+
- `monitor_cond`
|
|
40
|
+
- `monitor_cond_since`
|
|
41
|
+
- `monitor_cancel`
|
|
42
|
+
- `lock`
|
|
43
|
+
- `steal`
|
|
44
|
+
- `unlock`
|
|
45
|
+
- `echo`
|
|
46
|
+
- `set_db_change_aware`
|
|
47
|
+
- notifications: `update`, `update2`, `update3`, `locked`, `stolen`
|
|
30
48
|
|
|
31
|
-
|
|
32
|
-
// Connect to the OVSDB Unix socket
|
|
33
|
-
await client.connect();
|
|
34
|
-
console.log('Connected to OVSDB');
|
|
49
|
+
## Quick Start
|
|
35
50
|
|
|
36
|
-
|
|
37
|
-
const databases = await client.listDbs();
|
|
38
|
-
console.log('Databases:', databases);
|
|
51
|
+
Unix socket:
|
|
39
52
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
console.log('OVS Version:', schema.version);
|
|
53
|
+
```ts
|
|
54
|
+
import {OVSDBClient} from "@sourceregistry/node-ovsdb";
|
|
43
55
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
table: 'Bridge',
|
|
49
|
-
where: [],
|
|
50
|
-
columns: ['name']
|
|
51
|
-
}
|
|
52
|
-
];
|
|
56
|
+
const client = new OVSDBClient({
|
|
57
|
+
socketPath: "/var/run/openvswitch/db.sock",
|
|
58
|
+
timeout: 5000
|
|
59
|
+
});
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
try {
|
|
62
|
+
await client.connect();
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
const databases = await client.listDbs();
|
|
65
|
+
const schema = await client.getSchema("Open_vSwitch");
|
|
66
|
+
|
|
67
|
+
console.log(databases, schema.version);
|
|
68
|
+
} finally {
|
|
69
|
+
await client.close();
|
|
63
70
|
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Plain TCP:
|
|
64
74
|
|
|
65
|
-
|
|
75
|
+
```ts
|
|
76
|
+
import {OVSDBClient} from "@sourceregistry/node-ovsdb";
|
|
77
|
+
|
|
78
|
+
const client = new OVSDBClient({
|
|
79
|
+
host: "127.0.0.1",
|
|
80
|
+
port: 6640
|
|
81
|
+
});
|
|
66
82
|
```
|
|
67
83
|
|
|
68
|
-
|
|
84
|
+
TLS:
|
|
69
85
|
|
|
70
|
-
|
|
86
|
+
```ts
|
|
87
|
+
import {OVSDBClient} from "@sourceregistry/node-ovsdb";
|
|
71
88
|
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
const client = new OVSDBClient({
|
|
90
|
+
host: "ovsdb.example.internal",
|
|
91
|
+
port: 6640,
|
|
92
|
+
tls: true,
|
|
93
|
+
tlsOptions: {
|
|
94
|
+
servername: "ovsdb.example.internal",
|
|
95
|
+
rejectUnauthorized: true
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
```
|
|
74
99
|
|
|
75
|
-
|
|
76
|
-
// The 'using' statement ensures the client is closed even if an error occurs
|
|
77
|
-
using client = new OVSDBClient();
|
|
100
|
+
## Schema Generation
|
|
78
101
|
|
|
79
|
-
|
|
80
|
-
console.log('Connected to OVSDB');
|
|
102
|
+
The package includes an `ovsdb-generate` CLI that emits TypeScript row and database model types you can use with `OVSDBClient<...>`.
|
|
81
103
|
|
|
82
|
-
|
|
83
|
-
console.log('Available databases:', dbs);
|
|
84
|
-
}
|
|
104
|
+
Generate from a checked-in schema file:
|
|
85
105
|
|
|
86
|
-
|
|
106
|
+
```bash
|
|
107
|
+
npx ovsdb-generate --schema ./Open_vSwitch.schema.json --out ./src/generated/ovsdb.ts
|
|
87
108
|
```
|
|
88
109
|
|
|
89
|
-
|
|
110
|
+
Generate directly from a live OVSDB server:
|
|
90
111
|
|
|
91
|
-
|
|
112
|
+
```bash
|
|
113
|
+
npx ovsdb-generate --socket /var/run/openvswitch/db.sock --db Open_vSwitch --out ./src/generated/ovsdb.ts
|
|
114
|
+
```
|
|
92
115
|
|
|
93
|
-
|
|
94
|
-
- `listDbs()`: Retrieves the names of available databases.
|
|
95
|
-
- `getSchema(dbName)`: Retrieves the schema for a specific database.
|
|
96
|
-
- `transact(dbName, operations)`: Executes a series of database operations atomically.
|
|
97
|
-
- `monitor(...)`: Subscribes to database change notifications.
|
|
98
|
-
- `echo()`: Tests the connection liveness.
|
|
99
|
-
- `close()`: Closes the connection.
|
|
116
|
+
Generate from a live TCP endpoint:
|
|
100
117
|
|
|
101
|
-
|
|
118
|
+
```bash
|
|
119
|
+
npx ovsdb-generate --host 127.0.0.1 --port 6640 --db Open_vSwitch --out ./src/generated/ovsdb.ts
|
|
120
|
+
```
|
|
102
121
|
|
|
103
|
-
|
|
122
|
+
Generate from a live TLS endpoint:
|
|
104
123
|
|
|
105
|
-
|
|
124
|
+
```bash
|
|
125
|
+
npx ovsdb-generate \
|
|
126
|
+
--host ovsdb.example.internal \
|
|
127
|
+
--port 6640 \
|
|
128
|
+
--tls \
|
|
129
|
+
--tls-ca-file ./pki/ca.pem \
|
|
130
|
+
--tls-cert-file ./pki/client.pem \
|
|
131
|
+
--tls-key-file ./pki/client.key \
|
|
132
|
+
--db Open_vSwitch \
|
|
133
|
+
--out ./src/generated/ovsdb.ts
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
You can override the generated top-level type name with `--name OpenVSwitchDb`.
|
|
137
|
+
|
|
138
|
+
## Typed Transactions
|
|
139
|
+
|
|
140
|
+
You can provide your own table model to get typed table names, rows, selected columns, conditions, mutations, and tuple-shaped transaction results.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import {OVSDBClient, type DatabaseOperation, type OvsSet} from "@sourceregistry/node-ovsdb";
|
|
144
|
+
|
|
145
|
+
type OpenVSwitchDb = {
|
|
146
|
+
Bridge: {
|
|
147
|
+
name: string;
|
|
148
|
+
ports: OvsSet<string>;
|
|
149
|
+
};
|
|
150
|
+
Port: {
|
|
151
|
+
name: string;
|
|
152
|
+
interfaces: OvsSet<string>;
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const client = new OVSDBClient<OpenVSwitchDb>();
|
|
157
|
+
await client.connect();
|
|
158
|
+
|
|
159
|
+
const operations = [
|
|
160
|
+
{
|
|
161
|
+
op: "select",
|
|
162
|
+
table: "Bridge",
|
|
163
|
+
where: [["name", "==", "br-int"]],
|
|
164
|
+
columns: ["name", "ports"]
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
op: "insert",
|
|
168
|
+
table: "Port",
|
|
169
|
+
row: {
|
|
170
|
+
name: "uplink0",
|
|
171
|
+
interfaces: ["set", []]
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
] satisfies [DatabaseOperation<OpenVSwitchDb>, DatabaseOperation<OpenVSwitchDb>];
|
|
175
|
+
|
|
176
|
+
const [bridges, insertedPort] = await client.transact("Open_vSwitch", operations);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
For a higher-level staged flow, use `client.transaction(...)`. The callback can build operations against a transaction-scoped helper, and the library will send one `transact` request only if the callback completes successfully. By default it appends a trailing `commit` operation automatically.
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const outcome = await client.transaction("Open_vSwitch", (tx) => {
|
|
183
|
+
tx.comment("prepare bridge lookup");
|
|
184
|
+
tx.select({
|
|
185
|
+
op: "select",
|
|
186
|
+
table: "Bridge",
|
|
187
|
+
where: [["name", "==", "br-int"]],
|
|
188
|
+
columns: ["name"]
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return "ok";
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Monitoring
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import {OVSDBClient} from "@sourceregistry/node-ovsdb";
|
|
199
|
+
|
|
200
|
+
const client = new OVSDBClient();
|
|
201
|
+
await client.connect();
|
|
202
|
+
|
|
203
|
+
client.on("update", (notification) => {
|
|
204
|
+
const [monitorId, updates] = notification.params;
|
|
205
|
+
console.log("monitor", monitorId, updates);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await client.monitor("Open_vSwitch", "bridges", {
|
|
209
|
+
Bridge: {
|
|
210
|
+
columns: ["name"],
|
|
211
|
+
select: {
|
|
212
|
+
initial: true,
|
|
213
|
+
insert: true,
|
|
214
|
+
modify: true,
|
|
215
|
+
delete: true
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
For conditional monitoring, use `monitorCond()` or `monitorCondSince()`.
|
|
222
|
+
|
|
223
|
+
### Detect When an Interface Is Attached to a Bridge
|
|
106
224
|
|
|
107
|
-
|
|
225
|
+
OVSDB does not usually emit a single semantic event like "interface attached to bridge". Instead, you observe the row changes that together mean an attachment happened:
|
|
108
226
|
|
|
109
|
-
-
|
|
227
|
+
- a new `Interface` row may appear
|
|
228
|
+
- a new `Port` row may appear
|
|
229
|
+
- an existing `Bridge` row may be modified so its `ports` set now includes that port
|
|
110
230
|
|
|
111
|
-
|
|
231
|
+
In practice, the bridge update is usually the strongest signal that something was attached to the virtual switch.
|
|
112
232
|
|
|
113
|
-
|
|
114
|
-
- `npm run build`: Compile the TypeScript code and generate the distribution files in the `dist/` directory.
|
|
115
|
-
- `npm run preview`: Preview the built application.
|
|
116
|
-
- `npm run test`: Run the test suite with Vitest.
|
|
233
|
+
Why this works:
|
|
117
234
|
|
|
118
|
-
|
|
235
|
+
- the `Bridge.ports` column is the relationship that tells you which ports are attached to the bridge
|
|
236
|
+
- when that set grows, something new was connected to the bridge
|
|
237
|
+
- you can then inspect `Port` and `Interface` tables to resolve names or metadata for the newly attached objects
|
|
119
238
|
|
|
120
|
-
|
|
239
|
+
If you want richer correlation, monitor `Bridge`, `Port`, and `Interface` together and keep a small in-memory cache keyed by UUID so you can map a changed bridge port set back to the concrete port and interface names.
|
|
240
|
+
|
|
241
|
+
Example: [examples/detect-interface-added.ts](/Users/alexanderslaa/WebstormProjects/github.com/SourceRegistry/node-ovsdb/examples/detect-interface-added.ts)
|
|
242
|
+
|
|
243
|
+
## Common OVS Workflows
|
|
244
|
+
|
|
245
|
+
These examples focus on patterns that show up often in virtualized environments, where OVS is used to connect VM or container networking to a virtual switch.
|
|
246
|
+
|
|
247
|
+
### Create a Bridge With an Internal Interface
|
|
248
|
+
|
|
249
|
+
What this does:
|
|
250
|
+
|
|
251
|
+
- creates an `Interface` row of type `internal`
|
|
252
|
+
- creates a `Port` that owns that interface
|
|
253
|
+
- creates a `Bridge` that owns that port
|
|
254
|
+
|
|
255
|
+
Why it is done this way:
|
|
256
|
+
|
|
257
|
+
- in OVS, a bridge usually owns ports, and ports own interfaces
|
|
258
|
+
- creating all three rows in one transaction keeps the change atomic
|
|
259
|
+
- named UUIDs let later operations refer to rows inserted earlier in the same transaction
|
|
260
|
+
|
|
261
|
+
Example: [examples/bridge-port-interface.ts](/Users/alexanderslaa/WebstormProjects/github.com/SourceRegistry/node-ovsdb/examples/bridge-port-interface.ts)
|
|
262
|
+
|
|
263
|
+
### Attach a New Interface to an Existing Bridge
|
|
264
|
+
|
|
265
|
+
What this does:
|
|
266
|
+
|
|
267
|
+
- creates a new `Interface`
|
|
268
|
+
- creates a `Port` that references that interface
|
|
269
|
+
- mutates the existing bridge so the new port is added to its `ports` set
|
|
270
|
+
|
|
271
|
+
Why this is a common pattern:
|
|
272
|
+
|
|
273
|
+
- hypervisors and container hosts often attach new virtual NICs dynamically
|
|
274
|
+
- mutating the bridge `ports` set avoids rewriting the whole bridge row
|
|
275
|
+
- keeping it in one transaction prevents partial attachment state
|
|
276
|
+
|
|
277
|
+
Example: [examples/attach-interface-to-bridge.ts](/Users/alexanderslaa/WebstormProjects/github.com/SourceRegistry/node-ovsdb/examples/attach-interface-to-bridge.ts)
|
|
278
|
+
|
|
279
|
+
In practice, `type: "internal"` is useful when you want OVS itself to create the interface device. Leaving `type` unset is common when attaching an already existing device such as a tap interface created by a hypervisor.
|
|
280
|
+
|
|
281
|
+
## Resource Management
|
|
282
|
+
|
|
283
|
+
The client implements `AsyncDisposable`, so it also works with `await using` in runtimes that support explicit resource management.
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
await using client = new OVSDBClient();
|
|
287
|
+
await client.connect();
|
|
288
|
+
const dbs = await client.listDbs();
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Error Handling
|
|
292
|
+
|
|
293
|
+
- Transport/request failures reject with `Error`
|
|
294
|
+
- OVSDB JSON-RPC errors reject with `OvsdbRpcError`
|
|
295
|
+
- malformed inbound frames emit `protocolError`
|
|
296
|
+
- socket-level failures emit `transportError`
|
|
297
|
+
|
|
298
|
+
## Documentation
|
|
299
|
+
|
|
300
|
+
Generate API docs with Typedoc:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
npm run docs:build
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The public API is documented with TSDoc so the generated output is usable as a reference, not just a symbol dump.
|
|
307
|
+
|
|
308
|
+
## Roadmap
|
|
309
|
+
|
|
310
|
+
Planned work for the next iterations of the library:
|
|
311
|
+
|
|
312
|
+
- relation-aware schema generation so UUID reference columns can emit stronger types such as `PortRef` or `InterfaceRef` instead of plain `Uuid`
|
|
313
|
+
- richer codegen metadata for table relationships derived from `refTable` and `refType`
|
|
314
|
+
- helper utilities for working with generated reference types in transactions and monitor snapshots
|
|
315
|
+
- live TLS integration coverage for the transport and generator CLI
|
|
316
|
+
- stricter runtime validation for inbound notifications and response payloads
|
|
317
|
+
|
|
318
|
+
The intended direction is to make the generator more relation-aware first, before attempting a larger ORM-style layer.
|
|
319
|
+
|
|
320
|
+
## Development
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
npm test
|
|
324
|
+
npm run build
|
|
325
|
+
npm run docs:build
|
|
326
|
+
```
|
|
121
327
|
|
|
122
328
|
## License
|
|
123
329
|
|
|
124
|
-
|
|
330
|
+
Apache-2.0. See [LICENSE](./LICENSE).
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAwBA;;GAEG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAsD5D"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { OvsdbClientOptions } from './index';
|
|
2
|
+
import { DatabaseSchema } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Options for generating TypeScript types from an OVSDB schema.
|
|
5
|
+
*/
|
|
6
|
+
export interface GenerateTypesOptions {
|
|
7
|
+
/**
|
|
8
|
+
* The schema object to transform.
|
|
9
|
+
*/
|
|
10
|
+
schema: DatabaseSchema;
|
|
11
|
+
/**
|
|
12
|
+
* Name of the generated top-level database type.
|
|
13
|
+
*
|
|
14
|
+
* @defaultValue Derived from `schema.name`
|
|
15
|
+
*/
|
|
16
|
+
databaseTypeName?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Module specifier used in generated imports.
|
|
19
|
+
*
|
|
20
|
+
* @defaultValue `"@sourceregistry/node-ovsdb"`
|
|
21
|
+
*/
|
|
22
|
+
importFrom?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* CLI options for reading a schema and writing generated types.
|
|
26
|
+
*/
|
|
27
|
+
export interface GenerateTypesCliOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Path to a schema JSON file. When set, live OVSDB access is skipped.
|
|
30
|
+
*/
|
|
31
|
+
schemaPath?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Unix socket path for live schema introspection.
|
|
34
|
+
*/
|
|
35
|
+
socketPath?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Hostname for live schema introspection over TCP or TLS.
|
|
38
|
+
*/
|
|
39
|
+
host?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Port for live schema introspection over TCP or TLS.
|
|
42
|
+
*
|
|
43
|
+
* @defaultValue `6640`
|
|
44
|
+
*/
|
|
45
|
+
port?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Enables TLS for live schema introspection.
|
|
48
|
+
*/
|
|
49
|
+
tls?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Disables TLS peer verification for environments that use self-signed certificates.
|
|
52
|
+
*/
|
|
53
|
+
tlsInsecure?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Explicit TLS server name used during handshake verification.
|
|
56
|
+
*/
|
|
57
|
+
tlsServername?: string;
|
|
58
|
+
/**
|
|
59
|
+
* PEM CA bundle path used for TLS verification.
|
|
60
|
+
*/
|
|
61
|
+
tlsCaFile?: string;
|
|
62
|
+
/**
|
|
63
|
+
* PEM client certificate path used for mutual TLS.
|
|
64
|
+
*/
|
|
65
|
+
tlsCertFile?: string;
|
|
66
|
+
/**
|
|
67
|
+
* PEM client private key path used for mutual TLS.
|
|
68
|
+
*/
|
|
69
|
+
tlsKeyFile?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Database name used with live schema introspection.
|
|
72
|
+
*
|
|
73
|
+
* @defaultValue `"Open_vSwitch"`
|
|
74
|
+
*/
|
|
75
|
+
databaseName?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Destination file. When omitted, generated output is returned only.
|
|
78
|
+
*/
|
|
79
|
+
outputPath?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Name of the generated top-level database type.
|
|
82
|
+
*/
|
|
83
|
+
databaseTypeName?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Module specifier used in generated imports.
|
|
86
|
+
*
|
|
87
|
+
* @defaultValue `"@sourceregistry/node-ovsdb"`
|
|
88
|
+
*/
|
|
89
|
+
importFrom?: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Generates TypeScript types from an OVSDB schema.
|
|
93
|
+
*
|
|
94
|
+
* The output is designed for direct use as the generic database model for
|
|
95
|
+
* {@link OVSDBClient}.
|
|
96
|
+
*/
|
|
97
|
+
export declare function generateTypesFromSchema(options: GenerateTypesOptions): string;
|
|
98
|
+
/**
|
|
99
|
+
* Loads a schema from a file or a live OVSDB server and optionally writes the
|
|
100
|
+
* generated TypeScript output to disk.
|
|
101
|
+
*/
|
|
102
|
+
export declare function generateTypesFile(options: GenerateTypesCliOptions): Promise<string>;
|
|
103
|
+
/**
|
|
104
|
+
* Reads a schema JSON file from disk.
|
|
105
|
+
*/
|
|
106
|
+
export declare function readSchemaFile(schemaPath: string): Promise<DatabaseSchema>;
|
|
107
|
+
/**
|
|
108
|
+
* Reads a schema from a live OVSDB server.
|
|
109
|
+
*/
|
|
110
|
+
export declare function fetchSchemaFromOvsdb(options: {
|
|
111
|
+
socketPath?: string;
|
|
112
|
+
host?: string;
|
|
113
|
+
port?: number;
|
|
114
|
+
tls?: boolean;
|
|
115
|
+
tlsInsecure?: boolean;
|
|
116
|
+
tlsServername?: string;
|
|
117
|
+
tlsCaFile?: string;
|
|
118
|
+
tlsCertFile?: string;
|
|
119
|
+
tlsKeyFile?: string;
|
|
120
|
+
databaseName?: string;
|
|
121
|
+
}): Promise<DatabaseSchema>;
|
|
122
|
+
/**
|
|
123
|
+
* Builds client transport options for live schema introspection.
|
|
124
|
+
*/
|
|
125
|
+
export declare function createGeneratorClientOptions(options: {
|
|
126
|
+
socketPath?: string;
|
|
127
|
+
host?: string;
|
|
128
|
+
port?: number;
|
|
129
|
+
tls?: boolean;
|
|
130
|
+
tlsInsecure?: boolean;
|
|
131
|
+
tlsServername?: string;
|
|
132
|
+
tlsCaFile?: string;
|
|
133
|
+
tlsCertFile?: string;
|
|
134
|
+
tlsKeyFile?: string;
|
|
135
|
+
}): Promise<OvsdbClientOptions>;
|
|
136
|
+
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAChD,OAAO,KAAK,EAAuB,cAAc,EAAY,MAAM,SAAS,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC;;OAEG;IACH,MAAM,EAAE,cAAc,CAAC;IAEvB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACpC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CA4D7E;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAazF;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAGhF;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,cAAc,CAAC,CAS1B;AAqBD;;GAEG;AACH,wBAAsB,4BAA4B,CAAC,OAAO,EAAE;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAmB9B"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("node:fs"),m=require("node:net"),O=require("node:events"),q=require("node:tls");class f extends Error{response;constructor(t){super(t.details?`${t.error}: ${t.details}`:t.error),this.name="OvsdbRpcError",this.response=t}}class d extends Error{payload;constructor(t,e){super(t),this.name="OvsdbProtocolError",this.payload=e}}class w extends Error{operationIndex;operation;result;results;constructor(t){super(`Transaction operation ${t.operationIndex} failed: ${t.result.error}`),this.name="OvsdbTransactionError",this.operationIndex=t.operationIndex,this.operation=t.operation,this.result=t.result,this.results=t.results}}class g{stagedOperations=[];get operations(){return this.stagedOperations}add(t){return this.stagedOperations.push(t),t}insert(t){return this.add(t)}select(t){return this.add(t)}update(t){return this.add(t)}mutate(t){return this.add(t)}delete(t){return this.add(t)}wait(t){return this.add(t)}comment(t){return this.add({op:"comment",comment:t})}assert(t){return this.add({op:"assert",lock:t})}commit(t=!1){return this.add({op:"commit",durable:t})}abort(){return this.add({op:"abort"})}}class y extends O.EventEmitter{timeout;connectionOptions;connectionFactory;socket=null;requestId=1;receiveBuffer="";pendingRequests=new Map;connected=!1;closeEmitted=!1;constructor(t={}){super(),this.timeout=t.timeout??5e3,this.connectionOptions=E(t),this.connectionFactory=t.connectionFactory}get isConnected(){return this.connected}async connect(){if(this.connected)return this;if(!this.connectionFactory&&this.connectionOptions.transport==="unix"&&!v.existsSync(this.connectionOptions.socketPath))throw new Error(`OVSDB socket not found: ${this.connectionOptions.socketPath}`);const t=this.connectionFactory?this.connectionFactory(this.connectionOptions):k(this.connectionOptions);this.attachSocket(t);const e=this.connectionOptions.transport==="tls"?"secureConnect":"connect";return await new Promise((s,n)=>{const a=setTimeout(()=>{i(),this.disposeTransport(new Error(`Connection timeout after ${this.timeout}ms`)),n(new Error(`Connection timeout after ${this.timeout}ms`))},this.timeout),o=()=>{i(),this.connected=!0,this.closeEmitted=!1,this.emit("connect"),s()},c=h=>{i(),n(h)},i=()=>{clearTimeout(a),t.off(e,o),t.off("error",c)};t.once(e,o),t.once("error",c)}),this}async request(t,e=[]){this.assertConnected();const s=this.requestId++,n={method:t,params:e,id:s};return await new Promise((a,o)=>{const c=setTimeout(()=>{this.pendingRequests.delete(s),o(new Error(`Request timeout for method: ${t}`))},this.timeout);this.pendingRequests.set(s,{resolve:i=>a(i),reject:o,timeoutId:c}),this.writeMessage(n).catch(i=>{clearTimeout(c),this.pendingRequests.delete(s),o(i)})})}async notify(t,e=[]){this.assertConnected(),await this.writeMessage({method:t,params:e})}async listDbs(){return await this.request("list_dbs",[])}async getSchema(t="Open_vSwitch"){return await this.request("get_schema",[t])}async transact(t,e){return await this.request("transact",[t,...e])}async transaction(t,e,s={}){const n=new g,a=await e(n),o=[...n.operations],c=s.autoCommit??!0,i=o.some(u=>u.op==="commit"||u.op==="abort");if(c&&!i&&o.push({op:"commit",durable:s.durable??!1}),o.length===0)return{value:a,operations:o,results:[]};const h=await this.request("transact",[t,...o]);for(const[u,l]of h.entries())if(b(l))throw new w({operationIndex:u,operation:o[u],result:l,results:h});return{value:a,operations:o,results:h}}async cancel(t){return await this.request("cancel",[t])}async monitor(t,e,s){return await this.request("monitor",[t,e,s])}async monitorCond(t,e,s){return await this.request("monitor_cond",[t,e,s])}async monitorCondSince(t,e,s,n=null){return await this.request("monitor_cond_since",[t,e,s,n])}async monitorCancel(t){return await this.request("monitor_cancel",[t])}async lock(t){return await this.request("lock",[t])}async steal(t){return await this.request("steal",[t])}async unlock(t){return await this.request("unlock",[t])}async echo(...t){return await this.request("echo",t)}async setDbChangeAware(t=!0){return await this.request("set_db_change_aware",[t])}async close(){this.disposeTransport()}async[Symbol.asyncDispose](){await this.close()}attachSocket(t){this.socket=t,this.receiveBuffer="",t.on("data",this.handleData),t.on("error",this.handleSocketError),t.on("close",this.handleSocketClose)}handleData=t=>{this.receiveBuffer+=typeof t=="string"?t:t.toString("utf8");let e=p(this.receiveBuffer);for(;e;)this.receiveBuffer=e.rest,this.parseFrame(e.frame),e=p(this.receiveBuffer);const s=this.receiveBuffer.trimStart();s&&s[0]!=="{"&&s[0]!=="["&&(this.emitProtocolError("Received non-JSON data on the transport",this.receiveBuffer),this.receiveBuffer="")};handleSocketError=t=>{this.emit("transportError",t),this.disposeTransport(t)};handleSocketClose=()=>{this.disposeTransport()};parseFrame(t){try{const e=JSON.parse(t);this.handleMessage(e)}catch{const s=new d("Failed to parse JSON message",t);this.emit("protocolError",s,t)}}handleMessage(t){if(!t||typeof t!="object"){this.emitProtocolError("Expected a JSON object message",t);return}if("method"in t&&typeof t.method=="string"){const e=t;if(e.id!==void 0&&e.id!==null){this.handleIncomingRequest(e.method,e.params??[],e.id);return}this.handleNotification(t);return}if("id"in t){this.handleResponse(t);return}this.emitProtocolError("Received message without method or id",t)}handleResponse(t){const e=this.pendingRequests.get(t.id);if(!e){this.emitProtocolError("Received response for an unknown request id",t);return}if(this.pendingRequests.delete(t.id),clearTimeout(e.timeoutId),t.error){e.reject(new f(t.error));return}e.resolve(t.result)}async handleIncomingRequest(t,e,s){if(t==="echo"){await this.writeMessage({id:s,result:e,error:null});return}await this.writeMessage({id:s,result:null,error:{error:"not supported",details:`Unsupported server request: ${t}`}})}handleNotification(t){switch(this.emit("notification",t),t.method){case"update":this.emit("update",t);break;case"update2":this.emit("update2",t);break;case"update3":this.emit("update3",t);break;case"locked":this.emit("locked",t);break;case"stolen":this.emit("stolen",t);break;default:this.emitProtocolError("Received an unknown notification method",t);break}}emitProtocolError(t,e){const s=new d(t,e);this.emit("protocolError",s,e)}async writeMessage(t){this.assertConnected(),await new Promise((e,s)=>{this.socket?.write(`${JSON.stringify(t)}
|
|
2
|
+
`,n=>{if(n){s(n);return}e()})})}assertConnected(){if(!this.connected||!this.socket)throw new Error("Not connected to OVSDB")}disposeTransport(t){const e=this.socket;this.socket=null,this.connected=!1,this.receiveBuffer="",e&&(e.off("data",this.handleData),e.off("error",this.handleSocketError),e.off("close",this.handleSocketClose),e.destroyed||e.destroy());const s=t??new Error("Connection closed");for(const n of this.pendingRequests.values())clearTimeout(n.timeoutId),n.reject(s);this.pendingRequests.clear(),this.closeEmitted||(this.closeEmitted=!0,this.emit("close"))}}function E(r={}){if(r.host){const t=r.port??6640;return r.tls?{transport:"tls",host:r.host,port:t,tlsOptions:{host:r.host,port:t,...r.tlsOptions}}:{transport:"tcp",host:r.host,port:t}}return{transport:"unix",socketPath:r.socketPath??"/var/run/openvswitch/db.sock"}}function k(r){switch(r.transport){case"unix":return m.createConnection(r.socketPath);case"tcp":return m.createConnection(r.port,r.host);case"tls":return q.connect(r.tlsOptions)}}function b(r){return typeof r=="object"&&r!==null&&"error"in r&&typeof r.error=="string"}function p(r){let t=0;for(;t<r.length&&/\s/u.test(r[t]);)t+=1;if(t>=r.length)return null;const e=r[t];if(!(e==="{"?"}":e==="["?"]":null))return null;let n=0,a=!1,o=!1;for(let c=t;c<r.length;c+=1){const i=r[c];if(a){if(o){o=!1;continue}if(i==="\\"){o=!0;continue}i==='"'&&(a=!1);continue}if(i==='"'){a=!0;continue}if(i==="{"||i==="["){n+=1;continue}if((i==="}"||i==="]")&&(n-=1,n===0))return{frame:r.slice(t,c+1),rest:r.slice(c+1)}}return null}exports.OVSDBClient=y;exports.OvsdbProtocolError=d;exports.OvsdbRpcError=f;exports.OvsdbTransaction=g;exports.OvsdbTransactionError=w;exports.resolveConnectionOptions=E;
|
|
3
|
+
//# sourceMappingURL=index.cjs.map
|