@takodotid/circuit 0.43.1 → 1.0.0-rc.2
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 +115 -37
- package/package.json +21 -58
- package/src/checks/across.ts +402 -0
- package/src/checks/built-in.ts +256 -0
- package/src/checks/device.ts +436 -0
- package/src/checks/index.ts +53 -0
- package/src/cli/advisories.ts +108 -0
- package/src/cli/app.ts +71 -0
- package/src/cli/editor-types.generated.ts +30 -0
- package/src/cli/editor-types.ts +69 -0
- package/src/cli/format.ts +124 -0
- package/src/cli/git-hooks.ts +79 -0
- package/src/cli/index.ts +350 -1170
- package/src/cli/migrate.ts +554 -0
- package/src/cli/project-commands.ts +656 -0
- package/src/cli/releases.ts +374 -0
- package/src/cli/remember.ts +339 -0
- package/src/cli/rename.ts +27 -33
- package/src/cli/secrets-password.ts +60 -0
- package/src/cli/secrets.ts +258 -0
- package/src/cli/setup.ts +300 -0
- package/src/cli/signing.ts +23 -0
- package/src/cli/text-editor.ts +268 -0
- package/src/cli/ui.ts +298 -0
- package/src/cli/version.ts +2 -0
- package/src/cli/versions.ts +158 -0
- package/src/config/address.ts +102 -0
- package/src/config/circuit.d.ts +30 -0
- package/src/config/common.ts +65 -0
- package/src/config/communities.ts +479 -0
- package/src/config/define.ts +684 -0
- package/src/config/device.ts +164 -0
- package/src/config/firewall.ts +150 -0
- package/src/config/index.ts +22 -0
- package/src/{schema → config}/interfaces.ts +121 -85
- package/src/config/keys.ts +434 -0
- package/src/config/management.ts +61 -0
- package/src/config/merge.ts +335 -0
- package/src/config/models.ts +70 -0
- package/src/config/monitoring.ts +64 -0
- package/src/config/network.ts +180 -0
- package/src/config/presets.ts +149 -0
- package/src/config/routing.ts +401 -0
- package/src/config/services.ts +51 -0
- package/src/config/switching.ts +84 -0
- package/src/config/system.ts +103 -0
- package/src/{schema → config}/time-zones.ts +1 -1
- package/src/core/address.ts +1 -1
- package/src/core/cabling.ts +8 -43
- package/src/core/graph.ts +40 -0
- package/src/{integrations → core}/registries.ts +5 -3
- package/src/core/secrets-file.ts +539 -0
- package/src/core/secrets.ts +83 -64
- package/src/core/topology.ts +103 -146
- package/src/core/utils.ts +12 -1
- package/src/model.ts +83 -0
- package/src/platforms/derive.ts +64 -37
- package/src/platforms/finding.ts +9 -0
- package/src/platforms/fortios/firewall.ts +76 -64
- package/src/platforms/fortios/index.ts +189 -125
- package/src/platforms/fortios/names.ts +51 -18
- package/src/platforms/fortios/render.ts +69 -62
- package/src/platforms/fortios/routing.ts +89 -58
- package/src/platforms/fortios/vdoms.ts +47 -16
- package/src/platforms/junos/TRAPS.md +2 -2
- package/src/platforms/junos/bgp.ts +63 -42
- package/src/platforms/junos/index.ts +122 -89
- package/src/platforms/junos/render.ts +139 -104
- package/src/platforms/platform.ts +10 -8
- package/src/platforms/raisecom-ros/TRAPS.md +1 -1
- package/src/platforms/raisecom-ros/index.ts +89 -70
- package/src/platforms/raisecom-ros/render.ts +49 -45
- package/src/platforms/routeros/TRAPS.md +3 -3
- package/src/platforms/routeros/index.ts +121 -60
- package/src/platforms/routeros/render.ts +214 -195
- package/src/platforms/vrp/TRAPS.md +1 -1
- package/src/platforms/vrp/index.ts +74 -50
- package/src/platforms/vrp/render.ts +67 -56
- package/src/project/project.ts +725 -0
- package/src/studio/app/env.d.ts +9 -0
- package/src/studio/app/index.html +18 -0
- package/src/studio/app/src/App.vue +95 -0
- package/src/studio/app/src/components/AddressesView.vue +38 -0
- package/src/studio/app/src/components/CablesView.vue +29 -0
- package/src/studio/app/src/components/CompareView.vue +160 -0
- package/src/studio/app/src/components/DataTable.vue +49 -0
- package/src/studio/app/src/components/DeviceView.vue +237 -0
- package/src/studio/app/src/components/DevicesView.vue +32 -0
- package/src/studio/app/src/components/Faceplate.vue +75 -0
- package/src/studio/app/src/components/GraphView.vue +282 -0
- package/src/studio/app/src/components/LineDiff.vue +24 -0
- package/src/studio/app/src/components/ProblemsView.vue +42 -0
- package/src/studio/app/src/components/SearchView.vue +72 -0
- package/src/studio/app/src/components/SecretsView.vue +112 -0
- package/src/studio/app/src/components/SessionsView.vue +35 -0
- package/src/studio/app/src/components/Settings.vue +95 -0
- package/src/studio/app/src/components/ToolsView.vue +118 -0
- package/src/studio/app/src/components/VlansView.vue +50 -0
- package/src/studio/app/src/lib/csv.ts +19 -0
- package/src/studio/{public/diff.js → app/src/lib/diff.ts} +40 -35
- package/src/studio/app/src/lib/format.ts +38 -0
- package/src/studio/app/src/lib/net.ts +68 -0
- package/src/studio/app/src/lib/secrets-crypto.ts +35 -0
- package/src/studio/app/src/lib/store.ts +71 -0
- package/src/studio/app/src/lib/tools.ts +217 -0
- package/src/studio/app/src/main.ts +9 -0
- package/src/studio/{public → app/src}/style.css +14 -4
- package/src/studio/app/tsconfig.json +17 -0
- package/src/studio/app/vite.config.ts +16 -0
- package/src/studio/assets.generated.ts +3 -0
- package/src/studio/data.ts +96 -188
- package/src/studio/git.ts +27 -43
- package/src/studio/server.ts +196 -107
- package/src/studio/types.ts +141 -0
- package/src/transport/console.ts +3 -3
- package/src/transport/ssh.ts +1 -1
- package/dist/chunks/index-3b343xva.js +0 -177
- package/dist/chunks/index-6wb41q67.js +0 -62
- package/dist/chunks/index-n7w3tg5y.js +0 -149
- package/dist/chunks/index-yzw86rc8.js +0 -921
- package/dist/chunks/new-w3zys5y4.js +0 -172
- package/dist/chunks/server-cxr9ynxc.js +0 -259
- package/dist/cli/index.js +0 -12922
- package/dist/helpers/index.js +0 -881
- package/dist/index.js +0 -28
- package/dist/types/core/address-space.d.ts +0 -62
- package/dist/types/core/address.d.ts +0 -44
- package/dist/types/core/cabling.d.ts +0 -41
- package/dist/types/core/cross-checks.d.ts +0 -28
- package/dist/types/core/define.d.ts +0 -79
- package/dist/types/core/project-files.d.ts +0 -12
- package/dist/types/core/schema-docs.d.ts +0 -25
- package/dist/types/core/secrets.d.ts +0 -62
- package/dist/types/core/topology.d.ts +0 -96
- package/dist/types/core/utils.d.ts +0 -10
- package/dist/types/core/validator.d.ts +0 -23
- package/dist/types/helpers/bgp.d.ts +0 -195
- package/dist/types/helpers/cables.d.ts +0 -69
- package/dist/types/helpers/checks.d.ts +0 -36
- package/dist/types/helpers/exchange.d.ts +0 -55
- package/dist/types/helpers/filters.d.ts +0 -51
- package/dist/types/helpers/index.d.ts +0 -14
- package/dist/types/helpers/route-server.d.ts +0 -52
- package/dist/types/helpers/upstreams.d.ts +0 -192
- package/dist/types/index.d.ts +0 -10
- package/dist/types/integrations/librenms.d.ts +0 -60
- package/dist/types/integrations/netbox.d.ts +0 -89
- package/dist/types/integrations/peeringdb.d.ts +0 -24
- package/dist/types/integrations/prometheus.d.ts +0 -24
- package/dist/types/integrations/registries.d.ts +0 -8
- package/dist/types/platforms/bgp-wait.d.ts +0 -24
- package/dist/types/platforms/catalog.d.ts +0 -109
- package/dist/types/platforms/derive.d.ts +0 -17
- package/dist/types/platforms/fortios/firewall.d.ts +0 -47
- package/dist/types/platforms/fortios/index.d.ts +0 -140
- package/dist/types/platforms/fortios/models.d.ts +0 -14
- package/dist/types/platforms/fortios/names.d.ts +0 -52
- package/dist/types/platforms/fortios/plan.d.ts +0 -114
- package/dist/types/platforms/fortios/render.d.ts +0 -77
- package/dist/types/platforms/fortios/routing.d.ts +0 -56
- package/dist/types/platforms/fortios/tree.d.ts +0 -37
- package/dist/types/platforms/fortios/vdoms.d.ts +0 -50
- package/dist/types/platforms/index.d.ts +0 -13
- package/dist/types/platforms/junos/bgp.d.ts +0 -44
- package/dist/types/platforms/junos/index.d.ts +0 -114
- package/dist/types/platforms/junos/models.d.ts +0 -14
- package/dist/types/platforms/junos/render.d.ts +0 -154
- package/dist/types/platforms/line-diff.d.ts +0 -67
- package/dist/types/platforms/platform.d.ts +0 -135
- package/dist/types/platforms/raisecom-ros/index.d.ts +0 -71
- package/dist/types/platforms/raisecom-ros/models.d.ts +0 -14
- package/dist/types/platforms/raisecom-ros/render.d.ts +0 -41
- package/dist/types/platforms/routeros/export.d.ts +0 -59
- package/dist/types/platforms/routeros/index.d.ts +0 -116
- package/dist/types/platforms/routeros/models.d.ts +0 -18
- package/dist/types/platforms/routeros/plan.d.ts +0 -58
- package/dist/types/platforms/routeros/render.d.ts +0 -250
- package/dist/types/platforms/vrp/index.d.ts +0 -83
- package/dist/types/platforms/vrp/models.d.ts +0 -12
- package/dist/types/platforms/vrp/render.d.ts +0 -96
- package/dist/types/schema/common.d.ts +0 -30
- package/dist/types/schema/device.d.ts +0 -126
- package/dist/types/schema/firewall.d.ts +0 -131
- package/dist/types/schema/index.d.ts +0 -8
- package/dist/types/schema/interfaces.d.ts +0 -205
- package/dist/types/schema/policy.d.ts +0 -107
- package/dist/types/schema/routing.d.ts +0 -161
- package/dist/types/schema/system.d.ts +0 -198
- package/dist/types/schema/time-zones.d.ts +0 -2
- package/dist/types/transport/console.d.ts +0 -115
- package/dist/types/transport/path-check.d.ts +0 -32
- package/dist/types/transport/ssh.d.ts +0 -27
- package/src/cli/graph-format.ts +0 -91
- package/src/cli/new.ts +0 -234
- package/src/core/address-space.ts +0 -320
- package/src/core/cross-checks.ts +0 -369
- package/src/core/define.ts +0 -124
- package/src/core/project-files.ts +0 -35
- package/src/core/schema-docs.ts +0 -174
- package/src/core/validator.ts +0 -508
- package/src/helpers/bgp.ts +0 -473
- package/src/helpers/cables.ts +0 -74
- package/src/helpers/checks.ts +0 -201
- package/src/helpers/exchange.ts +0 -123
- package/src/helpers/filters.ts +0 -107
- package/src/helpers/index.ts +0 -25
- package/src/helpers/route-server.ts +0 -212
- package/src/helpers/upstreams.ts +0 -369
- package/src/index.ts +0 -13
- package/src/integrations/librenms.ts +0 -291
- package/src/integrations/netbox.ts +0 -620
- package/src/integrations/peeringdb.ts +0 -173
- package/src/integrations/prometheus.ts +0 -90
- package/src/schema/common.ts +0 -41
- package/src/schema/device.ts +0 -145
- package/src/schema/firewall.ts +0 -140
- package/src/schema/index.ts +0 -8
- package/src/schema/policy.ts +0 -115
- package/src/schema/routing.ts +0 -173
- package/src/schema/system.ts +0 -203
- package/src/studio/public/app.js +0 -186
- package/src/studio/public/compare.js +0 -250
- package/src/studio/public/dom.js +0 -138
- package/src/studio/public/evaluate.js +0 -687
- package/src/studio/public/export.js +0 -130
- package/src/studio/public/graph.js +0 -1541
- package/src/studio/public/index.html +0 -83
- package/src/studio/public/net.js +0 -134
- package/src/studio/public/tables.js +0 -1289
- package/src/studio/public/tools.js +0 -610
- package/templates/colocation/checks.ts +0 -53
- package/templates/colocation/circuit.config.ts +0 -9
- package/templates/colocation/edge-01.ts +0 -133
- package/templates/colocation/routing.ts +0 -13
- package/templates/colocation/tenancy.ts +0 -76
- package/templates/colocation/tenants/acme.ts +0 -19
- package/templates/colocation/tenants/index.ts +0 -6
- package/templates/colocation/tor-01.ts +0 -33
- package/templates/edge-router/checks.ts +0 -20
- package/templates/edge-router/circuit.config.ts +0 -8
- package/templates/edge-router/edge-01.ts +0 -110
- package/templates/edge-router/routing.ts +0 -13
- package/templates/project/AGENTS.md +0 -51
- package/templates/project/CLAUDE.md +0 -1
- package/templates/project/README.md +0 -16
- package/templates/project/gitignore +0 -5
- package/templates/project/tsconfig.json +0 -13
- package/templates/single-site/checks.ts +0 -26
- package/templates/single-site/circuit.config.ts +0 -8
- package/templates/single-site/router.ts +0 -86
- package/templates/single-site/site.ts +0 -18
- package/templates/single-site/switch.ts +0 -34
- /package/src/studio/{public → app/public}/favicon.svg +0 -0
- /package/src/studio/{public → app/public}/special-gothic-latin-ext.woff2 +0 -0
- /package/src/studio/{public → app/public}/special-gothic-latin.woff2 +0 -0
- /package/src/studio/{public → app/public}/special-gothic-license.txt +0 -0
|
@@ -3,16 +3,13 @@
|
|
|
3
3
|
import { Address } from "../../core/address";
|
|
4
4
|
import { Cabling } from "../../core/cabling";
|
|
5
5
|
import { Secrets } from "../../core/secrets";
|
|
6
|
-
import type { AclRule, Device, Ethertype, Family, Interface, Port, Ports, Rate, Severity, Vrrp } from "../../
|
|
6
|
+
import type { AclRule, Device, Ethertype, Family, Interface, Lag, Port, Ports, Rate, Severity, Vrrp } from "../../model";
|
|
7
7
|
import { Catalog } from "../catalog";
|
|
8
8
|
import { Derive } from "../derive";
|
|
9
9
|
import { JunosBgp } from "./bgp";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
type
|
|
13
|
-
type LagInterface = Extract<AnyInterface, { type: "lag" }>;
|
|
14
|
-
type Switched = AnyPort | LagInterface;
|
|
15
|
-
type AnyAclRule = AclRule<string>;
|
|
11
|
+
/** A port or a LAG: what switches VLANs. */
|
|
12
|
+
type Switched = Port | Lag;
|
|
16
13
|
|
|
17
14
|
/** Collects `set` statements, and comments the plan reads. */
|
|
18
15
|
export class JunosStatements {
|
|
@@ -51,49 +48,49 @@ export class JunosNames {
|
|
|
51
48
|
this.lags = Derive.lagIds(device);
|
|
52
49
|
|
|
53
50
|
// The default table's loopback is unit 0; one in a VRF takes the next unit.
|
|
54
|
-
const loopbacks =
|
|
51
|
+
const loopbacks = Derive.interfaces(device).filter((entry) => entry.kind === "loopback");
|
|
55
52
|
let unit = 0;
|
|
56
53
|
|
|
57
|
-
for (const
|
|
54
|
+
for (const { name, settings } of loopbacks) if (!settings.vrf) this.loopbacks.set(name, 0);
|
|
58
55
|
|
|
59
|
-
for (const
|
|
56
|
+
for (const { name, settings } of loopbacks) if (settings.vrf) this.loopbacks.set(name, ++unit);
|
|
60
57
|
|
|
61
58
|
// GRE tunnels are units of the one tunnel interface, in order.
|
|
62
|
-
const tunnels =
|
|
63
|
-
tunnels.forEach((
|
|
59
|
+
const tunnels = Derive.interfaces(device).filter((entry) => entry.kind === "gre");
|
|
60
|
+
tunnels.forEach(({ name }, index) => this.tunnels.set(name, index));
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
/** A physical port. A 10G cage holding a 1G optic is `ge-`, not `xe-`. */
|
|
67
64
|
port(port: string): string {
|
|
68
65
|
const name = Catalog.spell(this.device.platform, this.device.model, port);
|
|
69
|
-
const settings = this.device.
|
|
66
|
+
const settings = this.device.interfaces?.ethernet?.[port];
|
|
70
67
|
return settings?.speed === "1g" ? name.replace(/^xe-/, "ge-") : name;
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
/** The physical interface a port or a LAG is: `xe-0/0/1`, `ae1`. */
|
|
74
71
|
physical(name: string): string {
|
|
75
|
-
if (this.device.interfaces?.[name]
|
|
72
|
+
if (this.device.interfaces?.lag?.[name]) return `ae${this.lags.get(name)}`;
|
|
76
73
|
|
|
77
74
|
return this.port(name);
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
/** The logical interface that holds addresses: `xe-0/0/1.0`, `ae1.0`, `irb.10`, `lo0.0`. */
|
|
81
78
|
unit(name: string): string {
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
79
|
+
const vlanInterface = this.device.interfaces?.vlan_interface?.[name];
|
|
80
|
+
if (vlanInterface) return `irb.${this.device.switching!.vlans![vlanInterface.vlan]!.id}`;
|
|
84
81
|
|
|
85
|
-
if (
|
|
82
|
+
if (this.device.interfaces?.loopback?.[name]) return `lo0.${this.loopbacks.get(name)}`;
|
|
86
83
|
|
|
87
|
-
if (
|
|
84
|
+
if (this.device.interfaces?.gre?.[name]) return `gr-0/0/0.${this.tunnels.get(name)}`;
|
|
88
85
|
|
|
89
86
|
return `${this.physical(name)}.0`;
|
|
90
87
|
}
|
|
91
88
|
|
|
92
89
|
/** The physical ports behind a port or a LAG. */
|
|
93
90
|
members(name: string): string[] {
|
|
94
|
-
if (this.device.interfaces?.[name]
|
|
91
|
+
if (!this.device.interfaces?.lag?.[name]) return [this.port(name)];
|
|
95
92
|
|
|
96
|
-
const ports =
|
|
93
|
+
const ports = Derive.ports(this.device).filter(([, settings]) => settings.in_lag === name);
|
|
97
94
|
return ports.map(([port]) => this.port(port));
|
|
98
95
|
}
|
|
99
96
|
}
|
|
@@ -147,7 +144,7 @@ export class JunosRender {
|
|
|
147
144
|
"group-exchange-sha2",
|
|
148
145
|
];
|
|
149
146
|
|
|
150
|
-
/** The transport and port each management service listens on. */
|
|
147
|
+
/** The transport and port each management service listens on, by the name of its term in the services filter. */
|
|
151
148
|
private static readonly SERVICE_PORTS = {
|
|
152
149
|
ssh: ["tcp", 22],
|
|
153
150
|
telnet: ["tcp", 23],
|
|
@@ -267,14 +264,19 @@ export class JunosRender {
|
|
|
267
264
|
}
|
|
268
265
|
|
|
269
266
|
/** The family a hardware ACL rule is limited to, by its family or its addresses. */
|
|
270
|
-
private static aclFamily(rule:
|
|
271
|
-
const match = rule.
|
|
267
|
+
private static aclFamily(rule: AclRule): Family | undefined {
|
|
268
|
+
const match = rule.if;
|
|
272
269
|
if (match?.family) return match.family;
|
|
273
270
|
|
|
274
|
-
const address = match?.src ?? match?.dst;
|
|
271
|
+
const address = JunosRender.each(match?.src ?? match?.dst)[0];
|
|
275
272
|
return address ? Address.family(Address.host(address)) : undefined;
|
|
276
273
|
}
|
|
277
274
|
|
|
275
|
+
/** One value or a list of them, as a list. `unsupported()` refuses a list in an ACL rule's match, so a rule rendered for a device has one. */
|
|
276
|
+
private static each<T extends string>(value: T | readonly T[] | undefined): T[] {
|
|
277
|
+
return value === undefined ? [] : ([value].flat() as T[]);
|
|
278
|
+
}
|
|
279
|
+
|
|
278
280
|
/** The protocol as an IPv6 header names it. */
|
|
279
281
|
private static nextHeader(protocol: string): string {
|
|
280
282
|
return protocol === "icmp" ? "icmp6" : protocol;
|
|
@@ -297,7 +299,7 @@ export class JunosRender {
|
|
|
297
299
|
for (const server of system.ntp?.servers ?? []) this.out.set("system ntp server", server);
|
|
298
300
|
|
|
299
301
|
// Redirects are sent unless turned off, for each family.
|
|
300
|
-
if (!system.
|
|
302
|
+
if (!system.ip_stack?.icmp_redirects) {
|
|
301
303
|
this.out.set("system no-redirects");
|
|
302
304
|
this.out.set("system no-redirects-ipv6");
|
|
303
305
|
}
|
|
@@ -308,7 +310,7 @@ export class JunosRender {
|
|
|
308
310
|
if (local.files !== undefined) this.out.set("system syslog file messages archive files", local.files);
|
|
309
311
|
}
|
|
310
312
|
|
|
311
|
-
for (const remote of
|
|
313
|
+
for (const remote of this.device.monitoring?.syslog ?? []) {
|
|
312
314
|
this.out.set("system syslog host", remote.address, "any", JunosRender.LEVEL[remote.level]);
|
|
313
315
|
if (remote.port !== undefined && remote.port !== 514) this.out.set("system syslog host", remote.address, "port", remote.port);
|
|
314
316
|
}
|
|
@@ -316,7 +318,9 @@ export class JunosRender {
|
|
|
316
318
|
|
|
317
319
|
/** Every user but root logs in with a class. Root is the platform's own account and takes only its password and keys. */
|
|
318
320
|
private users(): void {
|
|
319
|
-
for (const [name, user] of Object.entries(this.device.users ?? {})) {
|
|
321
|
+
for (const [name, user] of Object.entries(this.device.system?.users ?? {})) {
|
|
322
|
+
if (!user) continue;
|
|
323
|
+
|
|
320
324
|
const login = name === "root" ? "system root-authentication" : `system login user ${name} authentication`;
|
|
321
325
|
|
|
322
326
|
if (name !== "root") {
|
|
@@ -357,7 +361,7 @@ export class JunosRender {
|
|
|
357
361
|
] as const;
|
|
358
362
|
|
|
359
363
|
for (const [service, statement] of others) {
|
|
360
|
-
const settings =
|
|
364
|
+
const settings = this.serviceSettings(service);
|
|
361
365
|
if (!settings) continue;
|
|
362
366
|
|
|
363
367
|
this.out.set(statement);
|
|
@@ -366,9 +370,17 @@ export class JunosRender {
|
|
|
366
370
|
}
|
|
367
371
|
}
|
|
368
372
|
|
|
373
|
+
/** A service's settings, by the name of its term in the services filter: SNMP is `monitoring.snmp`, NETCONF `management.native_protocol`. */
|
|
374
|
+
private serviceSettings(service: keyof typeof JunosRender.SERVICE_PORTS): { allow?: readonly string[]; port?: number } | undefined {
|
|
375
|
+
if (service === "snmp") return this.device.monitoring?.snmp;
|
|
376
|
+
if (service === "native") return this.device.management?.native_protocol;
|
|
377
|
+
|
|
378
|
+
return this.device.management?.[service];
|
|
379
|
+
}
|
|
380
|
+
|
|
369
381
|
/** SNMP version 2c with one read community, version 3 with users in one read-only group, or both. */
|
|
370
382
|
private snmp(): void {
|
|
371
|
-
const snmp = this.device.
|
|
383
|
+
const snmp = this.device.monitoring?.snmp;
|
|
372
384
|
if (!snmp) return;
|
|
373
385
|
|
|
374
386
|
if (snmp.contact) this.out.set("snmp contact", JunosRender.quote(snmp.contact));
|
|
@@ -391,13 +403,15 @@ export class JunosRender {
|
|
|
391
403
|
}
|
|
392
404
|
|
|
393
405
|
private vlans(): void {
|
|
394
|
-
for (const [name, vlan] of Object.entries(this.device.vlans ?? {})) {
|
|
406
|
+
for (const [name, vlan] of Object.entries(this.device.switching?.vlans ?? {})) {
|
|
407
|
+
if (!vlan) continue;
|
|
408
|
+
|
|
395
409
|
this.out.set(`vlans ${name} vlan-id`, vlan.id);
|
|
396
410
|
if (vlan.description) this.out.set(`vlans ${name} description`, JunosRender.quote(vlan.description));
|
|
397
411
|
}
|
|
398
412
|
|
|
399
|
-
for (const [name, iface] of Object.entries(this.device.interfaces ?? {})) {
|
|
400
|
-
if (iface
|
|
413
|
+
for (const [name, iface] of Object.entries(this.device.interfaces?.vlan_interface ?? {})) {
|
|
414
|
+
if (iface) this.out.set(`vlans ${iface.vlan} l3-interface`, this.names.unit(name));
|
|
401
415
|
}
|
|
402
416
|
}
|
|
403
417
|
|
|
@@ -416,7 +430,8 @@ export class JunosRender {
|
|
|
416
430
|
const members = new Set([...settings.trunk_vlans, ...(settings.native_vlan ? [settings.native_vlan] : [])]);
|
|
417
431
|
for (const vlan of members) this.out.set(family, "vlan members", vlan);
|
|
418
432
|
|
|
419
|
-
if (settings.native_vlan)
|
|
433
|
+
if (settings.native_vlan)
|
|
434
|
+
this.out.set(`interfaces ${physical} native-vlan-id`, this.device.switching!.vlans![settings.native_vlan]!.id);
|
|
420
435
|
}
|
|
421
436
|
|
|
422
437
|
const storm = JunosRender.stormProfile(settings);
|
|
@@ -439,10 +454,13 @@ export class JunosRender {
|
|
|
439
454
|
|
|
440
455
|
/** One filter for each set of kinds a port or LAG lets in: those kinds, then nothing else. */
|
|
441
456
|
private ethertypeFilters(): void {
|
|
442
|
-
const holders = [
|
|
457
|
+
const holders: Switched[] = [
|
|
458
|
+
...Derive.ports(this.device).map(([, settings]) => settings),
|
|
459
|
+
...this.lagSettings().map(([, lag]) => lag),
|
|
460
|
+
];
|
|
443
461
|
|
|
444
462
|
for (const settings of holders) {
|
|
445
|
-
if (!settings
|
|
463
|
+
if (!settings.ethertypes) continue;
|
|
446
464
|
|
|
447
465
|
const filter = `firewall family ethernet-switching filter ${JunosRender.ethertypeFilter(settings.ethertypes)}`;
|
|
448
466
|
for (const kind of JunosRender.ETHERTYPES.filter((kind) => settings.ethertypes!.includes(kind)))
|
|
@@ -453,7 +471,7 @@ export class JunosRender {
|
|
|
453
471
|
}
|
|
454
472
|
|
|
455
473
|
/** Addresses and VRRP groups of a logical interface such as `irb.10`. */
|
|
456
|
-
private addressing(unit: string, settings:
|
|
474
|
+
private addressing(unit: string, settings: Port | Interface): void {
|
|
457
475
|
const [physical, number] = unit.split(".");
|
|
458
476
|
const prefix = `interfaces ${physical} unit ${number}`;
|
|
459
477
|
const addresses = (settings.addresses ?? []).map((entry) => (typeof entry === "string" ? entry : entry.address));
|
|
@@ -477,7 +495,7 @@ export class JunosRender {
|
|
|
477
495
|
/** Every port the model has. One the config does not declare is turned off, and so is a 10G cage's name for the optic it does not hold. */
|
|
478
496
|
private ports(): void {
|
|
479
497
|
for (const port of Catalog.ports(this.device.platform, this.device.model)) {
|
|
480
|
-
const settings = this.device.
|
|
498
|
+
const settings = this.device.interfaces?.ethernet?.[port];
|
|
481
499
|
const name = this.names.port(port);
|
|
482
500
|
|
|
483
501
|
// A 10G cage is `xe-` with a 10G optic and `ge-` with a 1G one. The name not in use stays off, so a swapped optic does not bring up a port nobody configured.
|
|
@@ -491,8 +509,8 @@ export class JunosRender {
|
|
|
491
509
|
|
|
492
510
|
if (settings.description) this.out.set(`interfaces ${name} description`, JunosRender.quote(settings.description));
|
|
493
511
|
|
|
494
|
-
if (settings.
|
|
495
|
-
this.out.set(`interfaces ${name} ether-options 802.3ad`, `ae${this.names.lags.get(settings.
|
|
512
|
+
if (settings.in_lag) {
|
|
513
|
+
this.out.set(`interfaces ${name} ether-options 802.3ad`, `ae${this.names.lags.get(settings.in_lag)}`);
|
|
496
514
|
continue;
|
|
497
515
|
}
|
|
498
516
|
|
|
@@ -504,12 +522,15 @@ export class JunosRender {
|
|
|
504
522
|
}
|
|
505
523
|
}
|
|
506
524
|
|
|
525
|
+
/** The device's LAGs, each with its settings, in the order written. */
|
|
526
|
+
private lagSettings(): [string, Lag][] {
|
|
527
|
+
return Object.entries(this.device.interfaces?.lag ?? {}).flatMap(([name, lag]) => (lag ? [[name, lag] as [string, Lag]] : []));
|
|
528
|
+
}
|
|
529
|
+
|
|
507
530
|
private lags(): void {
|
|
508
531
|
let highest = -1;
|
|
509
532
|
|
|
510
|
-
for (const [name, lag] of
|
|
511
|
-
if (lag.type !== "lag") continue;
|
|
512
|
-
|
|
533
|
+
for (const [name, lag] of this.lagSettings()) {
|
|
513
534
|
const physical = this.names.physical(name);
|
|
514
535
|
highest = Math.max(highest, this.names.lags.get(name)!);
|
|
515
536
|
|
|
@@ -528,8 +549,8 @@ export class JunosRender {
|
|
|
528
549
|
|
|
529
550
|
/** VLAN interfaces, loopbacks and GRE tunnels: a description, a tunnel's ends, addresses and VRRP, and an MTU per family. */
|
|
530
551
|
private routedInterfaces(): void {
|
|
531
|
-
for (const
|
|
532
|
-
if (
|
|
552
|
+
for (const { name, kind, settings: iface } of Derive.interfaces(this.device)) {
|
|
553
|
+
if (kind !== "vlan_interface" && kind !== "loopback" && kind !== "gre") continue;
|
|
533
554
|
|
|
534
555
|
const unit = this.names.unit(name);
|
|
535
556
|
const [physical, number] = unit.split(".");
|
|
@@ -537,9 +558,10 @@ export class JunosRender {
|
|
|
537
558
|
|
|
538
559
|
if (iface.description) this.out.set(`${prefix} description`, JunosRender.quote(iface.description));
|
|
539
560
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
this.out.set(`${prefix} tunnel
|
|
561
|
+
const tunnel = kind === "gre" ? this.device.interfaces?.gre?.[name] : undefined;
|
|
562
|
+
if (tunnel) {
|
|
563
|
+
this.out.set(`${prefix} tunnel source`, Address.host(tunnel.local_address));
|
|
564
|
+
this.out.set(`${prefix} tunnel destination`, tunnel.remote_address);
|
|
543
565
|
}
|
|
544
566
|
|
|
545
567
|
this.addressing(unit, iface);
|
|
@@ -555,8 +577,10 @@ export class JunosRender {
|
|
|
555
577
|
/** One profile per threshold the ports and LAGs name. */
|
|
556
578
|
private stormProfiles(): void {
|
|
557
579
|
const switched: Switched[] = [
|
|
558
|
-
...
|
|
559
|
-
|
|
580
|
+
...Derive.ports(this.device)
|
|
581
|
+
.filter(([, settings]) => !settings.in_lag)
|
|
582
|
+
.map(([, settings]) => settings),
|
|
583
|
+
...this.lagSettings().map(([, lag]) => lag),
|
|
560
584
|
];
|
|
561
585
|
|
|
562
586
|
for (const settings of switched) {
|
|
@@ -578,14 +602,17 @@ export class JunosRender {
|
|
|
578
602
|
const management = this.device.management ?? {};
|
|
579
603
|
const guarded: { service: keyof typeof JunosRender.SERVICE_PORTS; sources: readonly string[] }[] = [];
|
|
580
604
|
|
|
605
|
+
// `management.allow` holds addresses by now: an address set's name is turned into its addresses before a platform reads it.
|
|
606
|
+
const allowed = typeof management.allow === "string" ? undefined : management.allow;
|
|
607
|
+
|
|
581
608
|
for (const service of Object.keys(JunosRender.SERVICE_PORTS) as (keyof typeof JunosRender.SERVICE_PORTS)[]) {
|
|
582
|
-
const settings =
|
|
583
|
-
const sources = settings?.allow ??
|
|
609
|
+
const settings = this.serviceSettings(service);
|
|
610
|
+
const sources = settings?.allow ?? allowed;
|
|
584
611
|
if (settings && sources?.length) guarded.push({ service, sources });
|
|
585
612
|
}
|
|
586
613
|
|
|
587
614
|
const ntp = this.device.system?.ntp;
|
|
588
|
-
const keepsTimeQuietly = !!ntp?.servers.length && !
|
|
615
|
+
const keepsTimeQuietly = !!ntp?.servers.length && !this.device.services?.ntp_server;
|
|
589
616
|
if (!guarded.length && !keepsTimeQuietly) return;
|
|
590
617
|
|
|
591
618
|
const families = [
|
|
@@ -599,7 +626,7 @@ export class JunosRender {
|
|
|
599
626
|
|
|
600
627
|
for (const { service, sources } of guarded) {
|
|
601
628
|
const [protocol, standardPort] = JunosRender.SERVICE_PORTS[service];
|
|
602
|
-
const port =
|
|
629
|
+
const port = this.serviceSettings(service)?.port ?? standardPort;
|
|
603
630
|
const term = `${filter} term ${service}`;
|
|
604
631
|
const allowed = sources.filter(ofFamily);
|
|
605
632
|
|
|
@@ -638,9 +665,9 @@ export class JunosRender {
|
|
|
638
665
|
|
|
639
666
|
const anchors = new Set(["lo0.0", ...[...this.names.loopbacks.values()].map((unit) => `lo0.${unit}`)]);
|
|
640
667
|
|
|
641
|
-
for (const [port, settings] of
|
|
668
|
+
for (const [port, settings] of Derive.ports(this.device)) {
|
|
642
669
|
const name = this.names.port(port);
|
|
643
|
-
if (settings
|
|
670
|
+
if (settings.addresses?.length && name.startsWith("em")) anchors.add(`${name}.0`);
|
|
644
671
|
}
|
|
645
672
|
|
|
646
673
|
for (const anchor of anchors) {
|
|
@@ -652,15 +679,15 @@ export class JunosRender {
|
|
|
652
679
|
|
|
653
680
|
/** A VRF is a virtual router: its own table, holding its interfaces. */
|
|
654
681
|
private vrfs(): void {
|
|
655
|
-
for (const [name, vrf] of Object.entries(this.device.vrfs ?? {})) {
|
|
682
|
+
for (const [name, vrf] of Object.entries(this.device.routing?.vrfs ?? {})) {
|
|
683
|
+
if (!vrf) continue;
|
|
684
|
+
|
|
656
685
|
this.out.set(`routing-instances ${name} instance-type virtual-router`);
|
|
657
686
|
if (vrf.description) this.out.set(`routing-instances ${name} description`, JunosRender.quote(vrf.description));
|
|
658
687
|
}
|
|
659
688
|
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
for (const [name, settings] of routed) {
|
|
663
|
-
if (settings?.vrf) this.out.set(`routing-instances ${settings.vrf} interface`, this.names.unit(name));
|
|
689
|
+
for (const [name, settings] of this.routed()) {
|
|
690
|
+
if (settings.vrf) this.out.set(`routing-instances ${settings.vrf} interface`, this.names.unit(name));
|
|
664
691
|
}
|
|
665
692
|
}
|
|
666
693
|
|
|
@@ -668,7 +695,7 @@ export class JunosRender {
|
|
|
668
695
|
private staticRoutes(): void {
|
|
669
696
|
if (this.device.routing?.router_id) this.out.set("routing-options router-id", this.device.routing.router_id);
|
|
670
697
|
|
|
671
|
-
for (const route of this.device.routing?.
|
|
698
|
+
for (const route of this.device.routing?.static_routes ?? []) {
|
|
672
699
|
const ipv6 = Address.family(Address.split(route.prefix).addr) === "ipv6";
|
|
673
700
|
const table = ipv6 ? ` rib ${route.vrf ? `${route.vrf}.` : ""}inet6.0` : "";
|
|
674
701
|
const options = route.vrf ? `routing-instances ${route.vrf} routing-options${table}` : `routing-options${table}`;
|
|
@@ -680,7 +707,7 @@ export class JunosRender {
|
|
|
680
707
|
continue;
|
|
681
708
|
}
|
|
682
709
|
|
|
683
|
-
const nextHop = route.
|
|
710
|
+
const nextHop = route.next_hop ? Address.host(route.next_hop) : this.names.unit(route.interface!);
|
|
684
711
|
if (route.distance === undefined) this.out.set(statement, "next-hop", nextHop);
|
|
685
712
|
else this.out.set(statement, "qualified-next-hop", nextHop, "preference", route.distance);
|
|
686
713
|
|
|
@@ -691,7 +718,7 @@ export class JunosRender {
|
|
|
691
718
|
|
|
692
719
|
/** Spanning tree on every switched port and LAG, each with its own settings. RSTP when no mode is given. */
|
|
693
720
|
private spanningTree(): void {
|
|
694
|
-
const stp = this.device.stp;
|
|
721
|
+
const stp = this.device.switching?.stp;
|
|
695
722
|
if (!stp) return;
|
|
696
723
|
|
|
697
724
|
// The platform has no plain STP; RSTP is held to it.
|
|
@@ -701,13 +728,12 @@ export class JunosRender {
|
|
|
701
728
|
|
|
702
729
|
const switched: [string, Switched][] = [];
|
|
703
730
|
|
|
704
|
-
for (const [port, settings] of
|
|
705
|
-
if (
|
|
706
|
-
switched.push([this.names.port(port), settings]);
|
|
731
|
+
for (const [port, settings] of Derive.ports(this.device)) {
|
|
732
|
+
if (!settings.in_lag && (settings.access_vlan || settings.trunk_vlans)) switched.push([this.names.port(port), settings]);
|
|
707
733
|
}
|
|
708
734
|
|
|
709
|
-
for (const [name, lag] of
|
|
710
|
-
if (lag.
|
|
735
|
+
for (const [name, lag] of this.lagSettings()) {
|
|
736
|
+
if (lag.access_vlan || lag.trunk_vlans) switched.push([this.names.physical(name), lag]);
|
|
711
737
|
}
|
|
712
738
|
|
|
713
739
|
for (const [name, settings] of switched) {
|
|
@@ -729,7 +755,7 @@ export class JunosRender {
|
|
|
729
755
|
* The device sends a port's SNMP index as its port ID unless told to send its name, so a neighbor sees `et-0/0/52` rather than `520`.
|
|
730
756
|
*/
|
|
731
757
|
private lldp(): void {
|
|
732
|
-
const lldp = this.device.lldp;
|
|
758
|
+
const lldp = this.device.switching?.lldp;
|
|
733
759
|
|
|
734
760
|
if (Cabling.runsAnywhere(this.device)) this.out.set("protocols lldp port-id-subtype interface-name");
|
|
735
761
|
|
|
@@ -737,9 +763,9 @@ export class JunosRender {
|
|
|
737
763
|
else if (lldp)
|
|
738
764
|
for (const name of lldp.interfaces) for (const port of this.names.members(name)) this.out.set("protocols lldp interface", port);
|
|
739
765
|
|
|
740
|
-
for (const [port, settings] of
|
|
741
|
-
if (settings
|
|
742
|
-
else if (settings
|
|
766
|
+
for (const [port, settings] of Derive.ports(this.device)) {
|
|
767
|
+
if (settings.lldp === false) this.out.set("protocols lldp interface", this.names.port(port), "disable");
|
|
768
|
+
else if (settings.lldp === true) this.out.set("protocols lldp interface", this.names.port(port));
|
|
743
769
|
}
|
|
744
770
|
}
|
|
745
771
|
|
|
@@ -748,18 +774,20 @@ export class JunosRender {
|
|
|
748
774
|
const ospf = this.device.routing?.ospf;
|
|
749
775
|
if (!ospf) return;
|
|
750
776
|
|
|
751
|
-
const protocols = (ospf.
|
|
777
|
+
const protocols = (ospf.address_families ?? ["ipv4"]).map((family) => (family === "ipv4" ? "ospf" : "ospf3"));
|
|
752
778
|
|
|
753
779
|
for (const protocol of protocols) {
|
|
754
780
|
for (const [areaId, area] of Object.entries(ospf.areas)) {
|
|
755
781
|
for (const [iface, settings] of Object.entries(area.interfaces)) {
|
|
782
|
+
if (!settings) continue;
|
|
783
|
+
|
|
756
784
|
const statement = `protocols ${protocol} area ${areaId} interface ${this.names.unit(iface)}`;
|
|
757
785
|
this.out.set(statement);
|
|
758
786
|
|
|
759
|
-
if (settings
|
|
760
|
-
if (settings
|
|
761
|
-
if (settings
|
|
762
|
-
if (settings
|
|
787
|
+
if (settings.cost !== undefined) this.out.set(statement, "metric", settings.cost);
|
|
788
|
+
if (settings.passive) this.out.set(statement, "passive");
|
|
789
|
+
if (settings.network === "point-to-point") this.out.set(statement, "interface-type p2p");
|
|
790
|
+
if (settings.bfd) JunosRender.bfd(this.out, statement, this.device);
|
|
763
791
|
}
|
|
764
792
|
}
|
|
765
793
|
|
|
@@ -775,10 +803,8 @@ export class JunosRender {
|
|
|
775
803
|
|
|
776
804
|
/** Each relay is a group of servers and the interface it listens on, in the table that interface routes in. */
|
|
777
805
|
private dhcpRelay(): void {
|
|
778
|
-
for (const [name, relay] of Object.entries(this.device.dhcp_relay ?? {})) {
|
|
779
|
-
const vrf =
|
|
780
|
-
this.device.interfaces?.[relay.interface]?.vrf ??
|
|
781
|
-
this.device.ports?.[relay.interface as keyof typeof this.device.ports]?.vrf;
|
|
806
|
+
for (const [name, relay] of Object.entries(this.device.services?.dhcp_relay ?? {})) {
|
|
807
|
+
const vrf = Derive.settings(this.device, relay.interface)?.vrf;
|
|
782
808
|
const relays = vrf ? `routing-instances ${vrf} forwarding-options dhcp-relay` : "forwarding-options dhcp-relay";
|
|
783
809
|
|
|
784
810
|
for (const server of relay.servers) this.out.set(`${relays} server-group ${name}`, server);
|
|
@@ -789,7 +815,7 @@ export class JunosRender {
|
|
|
789
815
|
|
|
790
816
|
/** sFlow from the management address, to each collector, on every declared port or those listed. A LAG is observed on its member ports. */
|
|
791
817
|
private sflow(): void {
|
|
792
|
-
const flow = this.device.flow_export;
|
|
818
|
+
const flow = this.device.monitoring?.flow_export;
|
|
793
819
|
if (flow?.protocol !== "sflow") return;
|
|
794
820
|
|
|
795
821
|
if (this.device.connection.host) this.out.set("protocols sflow agent-id", this.device.connection.host);
|
|
@@ -802,7 +828,7 @@ export class JunosRender {
|
|
|
802
828
|
}
|
|
803
829
|
}
|
|
804
830
|
|
|
805
|
-
const declared =
|
|
831
|
+
const declared = Derive.ports(this.device).filter(([, settings]) => !settings.in_lag);
|
|
806
832
|
const observed = flow.interfaces ?? declared.map(([port]) => port);
|
|
807
833
|
|
|
808
834
|
for (const name of observed) {
|
|
@@ -818,18 +844,19 @@ export class JunosRender {
|
|
|
818
844
|
const switched = new Set<string>();
|
|
819
845
|
const routed = new Set<string>();
|
|
820
846
|
|
|
821
|
-
for (const [port, settings] of
|
|
822
|
-
|
|
847
|
+
for (const [port, settings] of Derive.ports(this.device)) {
|
|
848
|
+
const acl = settings.ingress_acl;
|
|
849
|
+
if (!acl) continue;
|
|
823
850
|
|
|
824
851
|
const physical = this.names.port(port);
|
|
825
852
|
|
|
826
853
|
if (settings.access_vlan || settings.trunk_vlans) {
|
|
827
|
-
switched.add(
|
|
828
|
-
this.out.set(`interfaces ${physical} unit 0 family ethernet-switching filter input`,
|
|
854
|
+
switched.add(acl);
|
|
855
|
+
this.out.set(`interfaces ${physical} unit 0 family ethernet-switching filter input`, acl);
|
|
829
856
|
} else {
|
|
830
|
-
routed.add(
|
|
831
|
-
this.out.set(`interfaces ${physical} unit 0 family inet filter input`, `${
|
|
832
|
-
this.out.set(`interfaces ${physical} unit 0 family inet6 filter input`, `${
|
|
857
|
+
routed.add(acl);
|
|
858
|
+
this.out.set(`interfaces ${physical} unit 0 family inet filter input`, `${acl}-v4`);
|
|
859
|
+
this.out.set(`interfaces ${physical} unit 0 family inet6 filter input`, `${acl}-v6`);
|
|
833
860
|
}
|
|
834
861
|
}
|
|
835
862
|
|
|
@@ -842,8 +869,8 @@ export class JunosRender {
|
|
|
842
869
|
}
|
|
843
870
|
|
|
844
871
|
/** A rule's decision, through a policer when it has a rate. */
|
|
845
|
-
private aclThen(term: string, policer: string, rule:
|
|
846
|
-
if (rule.
|
|
872
|
+
private aclThen(term: string, policer: string, rule: AclRule): void {
|
|
873
|
+
if (rule.then === "drop") {
|
|
847
874
|
this.out.set(term, "then discard");
|
|
848
875
|
return;
|
|
849
876
|
}
|
|
@@ -861,8 +888,8 @@ export class JunosRender {
|
|
|
861
888
|
private switchingFilter(acl: string): void {
|
|
862
889
|
const filter = `firewall family ethernet-switching filter ${acl}`;
|
|
863
890
|
|
|
864
|
-
(this.device.acls?.[acl] ?? []).forEach((rule, index) => {
|
|
865
|
-
const match = rule.
|
|
891
|
+
(this.device.switching?.acls?.[acl] ?? []).forEach((rule, index) => {
|
|
892
|
+
const match = rule.if ?? {};
|
|
866
893
|
const addressed = match.src || match.dst || match.protocol || match.src_port !== undefined || match.dst_port !== undefined;
|
|
867
894
|
const family = JunosRender.aclFamily(rule);
|
|
868
895
|
const families = addressed || family ? (family ? [family] : (["ipv4", "ipv6"] as const)) : [undefined];
|
|
@@ -875,8 +902,10 @@ export class JunosRender {
|
|
|
875
902
|
const version = `from ip-version ${each}`;
|
|
876
903
|
const v4 = each === "ipv4";
|
|
877
904
|
|
|
878
|
-
|
|
879
|
-
|
|
905
|
+
for (const src of JunosRender.each(match.src))
|
|
906
|
+
this.out.set(term, version, v4 ? "ip-source-address" : "ip6-source-address", src);
|
|
907
|
+
for (const dst of JunosRender.each(match.dst))
|
|
908
|
+
this.out.set(term, version, v4 ? "ip-destination-address" : "ip6-destination-address", dst);
|
|
880
909
|
if (match.protocol)
|
|
881
910
|
this.out.set(
|
|
882
911
|
term,
|
|
@@ -902,17 +931,17 @@ export class JunosRender {
|
|
|
902
931
|
const name = `${acl}-${family === "ipv4" ? "v4" : "v6"}`;
|
|
903
932
|
const filter = `firewall family ${family === "ipv4" ? "inet" : "inet6"} filter ${name}`;
|
|
904
933
|
|
|
905
|
-
(this.device.acls?.[acl] ?? []).forEach((rule, index) => {
|
|
934
|
+
(this.device.switching?.acls?.[acl] ?? []).forEach((rule, index) => {
|
|
906
935
|
const ruleFamily = JunosRender.aclFamily(rule);
|
|
907
936
|
if (ruleFamily && ruleFamily !== family) return;
|
|
908
937
|
|
|
909
|
-
const match = rule.
|
|
938
|
+
const match = rule.if ?? {};
|
|
910
939
|
const label = `${index + 1}`;
|
|
911
940
|
const term = `${filter} term ${JunosRender.ruleName(label, rule.description)}`;
|
|
912
941
|
const protocolWord = family === "ipv4" ? "protocol" : "next-header";
|
|
913
942
|
|
|
914
|
-
|
|
915
|
-
|
|
943
|
+
for (const src of JunosRender.each(match.src)) this.out.set(term, "from source-address", src);
|
|
944
|
+
for (const dst of JunosRender.each(match.dst)) this.out.set(term, "from destination-address", dst);
|
|
916
945
|
if (match.protocol)
|
|
917
946
|
this.out.set(term, "from", protocolWord, family === "ipv4" ? match.protocol : JunosRender.nextHeader(match.protocol));
|
|
918
947
|
for (const port of match.src_port !== undefined ? JunosRender.eachPort(match.src_port) : [])
|
|
@@ -928,7 +957,7 @@ export class JunosRender {
|
|
|
928
957
|
|
|
929
958
|
/** A DHCP server is an address pool with its network, range, options and reservations, served on its interface. */
|
|
930
959
|
private dhcpServers(): void {
|
|
931
|
-
for (const [name, server] of Object.entries(this.device.
|
|
960
|
+
for (const [name, server] of Object.entries(this.device.services?.dhcp_server ?? {})) {
|
|
932
961
|
this.out.set(`system services dhcp-local-server group ${name} interface`, this.names.unit(server.interface));
|
|
933
962
|
|
|
934
963
|
const pool = `access address-assignment pool ${name} family inet`;
|
|
@@ -952,10 +981,8 @@ export class JunosRender {
|
|
|
952
981
|
|
|
953
982
|
/** Router advertisements announce each IPv6 network of the interface. */
|
|
954
983
|
private routerAdvertisements(): void {
|
|
955
|
-
const
|
|
956
|
-
|
|
957
|
-
for (const [name, settings] of routed) {
|
|
958
|
-
if (!settings?.ipv6_ra) continue;
|
|
984
|
+
for (const [name, settings] of this.routed()) {
|
|
985
|
+
if (!settings.ipv6_ra) continue;
|
|
959
986
|
|
|
960
987
|
const addresses = (settings.addresses ?? []).map((entry) => (typeof entry === "string" ? entry : entry.address));
|
|
961
988
|
|
|
@@ -964,4 +991,12 @@ export class JunosRender {
|
|
|
964
991
|
}
|
|
965
992
|
}
|
|
966
993
|
}
|
|
994
|
+
|
|
995
|
+
/** Every port, then every other interface kind by kind, each with its settings. */
|
|
996
|
+
private routed(): [string, Port | Interface][] {
|
|
997
|
+
return [
|
|
998
|
+
...Derive.ports(this.device),
|
|
999
|
+
...Derive.interfaces(this.device).map(({ name, settings }): [string, Interface] => [name, settings]),
|
|
1000
|
+
];
|
|
1001
|
+
}
|
|
967
1002
|
}
|