@takodotid/circuit 0.4.3 → 0.4.7
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takodotid/circuit",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "Declarative, vendor-neutral network configuration: describe each device in TypeScript, and Circuit makes the device match. Alpha.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"network",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
".": "./src/index.ts"
|
|
43
43
|
},
|
|
44
44
|
"bin": {
|
|
45
|
-
"circuit": "
|
|
45
|
+
"circuit": "src/cli/index.ts"
|
|
46
46
|
},
|
|
47
47
|
"files": [
|
|
48
48
|
"src",
|
|
@@ -10,6 +10,8 @@ export const INSTALL_CERTIFICATE = "#install-certificate ";
|
|
|
10
10
|
|
|
11
11
|
/** Dependency order. A menu not listed sorts after all of these. The longest matching prefix decides. */
|
|
12
12
|
const ORDER = [
|
|
13
|
+
// Certificates depend on nothing, and services name them.
|
|
14
|
+
"/certificate",
|
|
13
15
|
"/interface bridge",
|
|
14
16
|
"/interface ethernet",
|
|
15
17
|
"/interface bonding",
|
|
@@ -42,7 +44,6 @@ const ORDER = [
|
|
|
42
44
|
"/ipv6",
|
|
43
45
|
"/routing",
|
|
44
46
|
"/interface ethernet switch",
|
|
45
|
-
"/certificate",
|
|
46
47
|
"/snmp",
|
|
47
48
|
"/tool",
|
|
48
49
|
"/user",
|
|
@@ -257,7 +258,7 @@ function planEntries(menu: string, wanted: Command[], existing: Command[], sendS
|
|
|
257
258
|
}
|
|
258
259
|
|
|
259
260
|
/**
|
|
260
|
-
* Certificates are installed from the config when missing or different, after the old one of that name is removed. One the config does not hold is removed in the last phase.
|
|
261
|
+
* Certificates are installed from the config when missing or different, after the old one of that name is removed. One the device holds under another name, with the same fingerprint and key, is renamed rather than imported a second time. One the config does not hold is removed in the last phase.
|
|
261
262
|
*/
|
|
262
263
|
function planCertificates(wanted: Command[], existing: Command[]): MenuPlan {
|
|
263
264
|
const menu = "/certificate";
|
|
@@ -265,19 +266,37 @@ function planCertificates(wanted: Command[], existing: Command[]): MenuPlan {
|
|
|
265
266
|
const install: Step = { title: menu, show: [], send: [] };
|
|
266
267
|
const remove: Step = { title: menu, show: [], send: [] };
|
|
267
268
|
const existingByName = new Map(existing.map((item) => [item.attrs.name, item]));
|
|
269
|
+
const wantedNames = new Set(wanted.map((item) => item.attrs.name));
|
|
270
|
+
const renamed = new Set<string>();
|
|
268
271
|
|
|
269
272
|
for (const entry of wanted) {
|
|
270
273
|
const name = entry.attrs.name!;
|
|
271
274
|
const current = existingByName.get(name);
|
|
272
275
|
if (current && canonical(current) === canonical(entry)) continue;
|
|
273
276
|
|
|
277
|
+
const sameCertificate = existing.find(
|
|
278
|
+
(item) =>
|
|
279
|
+
!wantedNames.has(item.attrs.name) &&
|
|
280
|
+
!renamed.has(item.attrs.name!) &&
|
|
281
|
+
item.attrs.fingerprint === entry.attrs.fingerprint &&
|
|
282
|
+
item.attrs["private-key"] === entry.attrs["private-key"]
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
if (!current && sameCertificate) {
|
|
286
|
+
const oldName = sameCertificate.attrs.name!;
|
|
287
|
+
renamed.add(oldName);
|
|
288
|
+
install.show.push(`~ rename ${oldName} to ${name}`);
|
|
289
|
+
install.send.push(`${menu} set [ find name=${quote(oldName)} ] name=${quote(name)}`);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
|
|
274
293
|
install.show.push(`${current ? "~" : "+"} install ${name}`);
|
|
275
294
|
if (current) install.send.push(`${menu} remove [ find name=${quote(name)} ]`);
|
|
276
295
|
install.send.push(INSTALL_CERTIFICATE + name);
|
|
277
296
|
}
|
|
278
297
|
|
|
279
298
|
for (const entry of existing) {
|
|
280
|
-
if (
|
|
299
|
+
if (wantedNames.has(entry.attrs.name) || renamed.has(entry.attrs.name!)) continue;
|
|
281
300
|
|
|
282
301
|
remove.show.push(`- ${entry.attrs.name}`);
|
|
283
302
|
remove.send.push(`${menu} remove [ find name=${quote(entry.attrs.name!)} ]`);
|
|
@@ -20,3 +20,4 @@ Behaviour that looks like something else. Each one has been hit on a CE6855 runn
|
|
|
20
20
|
- `abort` is not a command here. `clear configuration candidate` discards uncommitted changes.
|
|
21
21
|
- sFlow samples per port, `sflow sampling rate N`, N from 4096; the default is 8192 and an explicit 8192 is kept as written. A port names a collector only once the collector exists globally; until then it is refused with "collector does not exist".
|
|
22
22
|
- `vlan batch` holds every VLAN on one line, and undoing a list that names a VLAN with an L3 interface is refused. A change sends only the VLANs added and `undo vlan batch` for the ones removed.
|
|
23
|
+
- A VLAN name is unique across VLANs: a new VLAN cannot take a name another still holds. VLANs no longer used, and their VLAN interfaces, are removed first, in the same candidate.
|
|
@@ -722,21 +722,52 @@ export async function apply(device: Device, _network: readonly Device[], plan: P
|
|
|
722
722
|
console.log(" committed, login proved, saved");
|
|
723
723
|
}
|
|
724
724
|
|
|
725
|
+
/**
|
|
726
|
+
* What changes, in the order the platform accepts.
|
|
727
|
+
*
|
|
728
|
+
* A VLAN list changes by ID. VLANs no longer used go first, with their VLAN interfaces, so their names and numbers are free before a new VLAN takes them: a VLAN name is unique, and a VLAN with an interface cannot be removed. The whole plan is one candidate, so going first leaves nothing half done.
|
|
729
|
+
*/
|
|
730
|
+
function plan(desired: string, current: string): Plan {
|
|
731
|
+
const groups = compare(desired, current, dialect);
|
|
732
|
+
vlanListChange(
|
|
733
|
+
groups,
|
|
734
|
+
"vlan batch ",
|
|
735
|
+
batchIds,
|
|
736
|
+
(ids) => [`vlan batch ${vlanBatch(ids)}`],
|
|
737
|
+
(ids) => [`vlan batch ${vlanBatch(ids)}`]
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
const top = groups.find((group) => group.path.length === 0);
|
|
741
|
+
const removedBatch = top?.remove.find((line) => line.startsWith("vlan batch ")) ?? "";
|
|
742
|
+
const removedIds = removedBatch ? batchIds(removedBatch) : [];
|
|
743
|
+
|
|
744
|
+
const firstRemovals = [
|
|
745
|
+
...removedIds.map((id) => `interface Vlanif${id}`).filter((line) => top?.remove.includes(line)),
|
|
746
|
+
...removedIds.map((id) => `vlan ${id}`).filter((line) => top?.remove.includes(line)),
|
|
747
|
+
...(removedBatch ? [removedBatch] : []),
|
|
748
|
+
];
|
|
749
|
+
|
|
750
|
+
if (top) top.remove = top.remove.filter((line) => !firstRemovals.includes(line));
|
|
751
|
+
|
|
752
|
+
const planned = steps(
|
|
753
|
+
groups.filter((group) => group.add.length || group.remove.length),
|
|
754
|
+
dialect
|
|
755
|
+
);
|
|
756
|
+
|
|
757
|
+
if (firstRemovals.length) {
|
|
758
|
+
// `undo vlan batch` removes a VLAN's own settings with it, so only the interfaces and the list are sent.
|
|
759
|
+
const sent = firstRemovals.filter((line) => !/^vlan \d+$/.test(line)).map(undo);
|
|
760
|
+
planned.unshift({ title: "VLANs no longer used", show: firstRemovals.map((line) => `- ${line}`), send: sent });
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return { steps: planned, problems: [] };
|
|
764
|
+
}
|
|
765
|
+
|
|
725
766
|
export const vrp: DeviceAdapter = {
|
|
726
767
|
extension: ".cfg",
|
|
727
768
|
unsupported,
|
|
728
769
|
render,
|
|
729
770
|
read,
|
|
730
|
-
plan
|
|
731
|
-
const groups = compare(desired, current, dialect);
|
|
732
|
-
vlanListChange(
|
|
733
|
-
groups,
|
|
734
|
-
"vlan batch ",
|
|
735
|
-
batchIds,
|
|
736
|
-
(ids) => [`vlan batch ${vlanBatch(ids)}`],
|
|
737
|
-
(ids) => [`vlan batch ${vlanBatch(ids)}`]
|
|
738
|
-
);
|
|
739
|
-
return { steps: steps(groups, dialect), problems: [] };
|
|
740
|
-
},
|
|
771
|
+
plan,
|
|
741
772
|
apply,
|
|
742
773
|
};
|
package/src/cli/index.ts
CHANGED
|
File without changes
|