packet-tracer-skill 0.2.0 → 0.2.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/CHANGELOG.md +42 -4
- package/README.md +237 -88
- package/docs/campus-donor-proof.md +103 -0
- package/docs/curated-donor-registry.md +26 -0
- package/docs/github-launch-ops-0.2.1.md +45 -0
- package/docs/github-launch-ops-0.2.2.md +42 -0
- package/docs/github-metadata.md +23 -6
- package/docs/hero-demo-plan.md +18 -10
- package/docs/home-iot-donor-proof.md +64 -0
- package/docs/launch-announcement-0.2.1.md +15 -0
- package/docs/launch-announcement-0.2.2.md +15 -0
- package/docs/packet-tracer-feature-gap-atlas.md +87 -0
- package/docs/post-launch-follow-up.md +39 -0
- package/docs/publish-preview-roadmap.md +20 -17
- package/docs/release-checklist.md +22 -12
- package/docs/{release-notes-0.2.0.md → release-notes-0.2.1.md} +7 -6
- package/docs/release-notes-0.2.2.md +26 -0
- package/docs/runtime-truth.md +31 -0
- package/docs/wan-security-donor-proof.md +41 -0
- package/docs/wireless-advanced-proof.md +60 -0
- package/examples/README.md +13 -10
- package/examples/gallery.md +10 -4
- package/examples/index.json +3 -3
- package/package.json +44 -16
- package/references/curated-donor-registry.json +5 -2
- package/references/packettracer-feature-atlas.json +133 -0
- package/references/scenario-fixture-corpus.json +1 -0
- package/scripts/build_examples_index.py +27 -16
- package/scripts/coverage_matrix.py +559 -23
- package/scripts/feature_atlas.py +229 -0
- package/scripts/generate_pkt.py +330 -31
- package/scripts/intent_parser.py +278 -7
- package/scripts/pkt_editor.py +174 -0
- package/scripts/runtime_doctor.py +83 -10
- package/scripts/sample_catalog.py +210 -5
- package/docs/screenshots/.gitkeep +0 -1
- package/docs/screenshots/packet-tracer-topology-cropped.png +0 -0
- package/scripts/__pycache__/build_examples_index.cpython-314.pyc +0 -0
- package/scripts/__pycache__/build_sample_catalog.cpython-314.pyc +0 -0
- package/scripts/__pycache__/coverage_matrix.cpython-314.pyc +0 -0
- package/scripts/__pycache__/donor_diagnostics.cpython-314.pyc +0 -0
- package/scripts/__pycache__/generate_pkt.cpython-314.pyc +0 -0
- package/scripts/__pycache__/install_skill.cpython-314.pyc +0 -0
- package/scripts/__pycache__/intent_parser.cpython-314.pyc +0 -0
- package/scripts/__pycache__/packet_tracer_env.cpython-314.pyc +0 -0
- package/scripts/__pycache__/pkt_builder.cpython-314.pyc +0 -0
- package/scripts/__pycache__/pkt_codec.cpython-314.pyc +0 -0
- package/scripts/__pycache__/pkt_editor.cpython-314.pyc +0 -0
- package/scripts/__pycache__/pkt_transformer.cpython-314.pyc +0 -0
- package/scripts/__pycache__/remote_search.cpython-314.pyc +0 -0
- package/scripts/__pycache__/runtime_doctor.cpython-314.pyc +0 -0
- package/scripts/__pycache__/sample_catalog.cpython-314.pyc +0 -0
- package/scripts/__pycache__/sample_selector.cpython-314.pyc +0 -0
- package/scripts/__pycache__/twofish_diagnostics.cpython-314.pyc +0 -0
- package/scripts/__pycache__/twofish_runtime.cpython-314.pyc +0 -0
- package/scripts/__pycache__/workspace_repair.cpython-314.pyc +0 -0
- package/scripts/vendor/__pycache__/twofish.cpython-314.pyc +0 -0
package/scripts/intent_parser.py
CHANGED
|
@@ -28,7 +28,7 @@ CAPABILITY_PATTERNS = {
|
|
|
28
28
|
"management_vlan": [r"\bmanagement vlan\b", r"\bvlan 99\b", r"\bdefault-gateway\b"],
|
|
29
29
|
"telnet": [r"\btelnet\b", r"\bvty\b", r"\btransport input telnet\b"],
|
|
30
30
|
"wireless_ap": [r"\bssid\b", r"\bwifi\b", r"\bwpa\b", r"\bwpa2\b", r"\bwireless\b", r"\baccess point\b", r"\bap\b"],
|
|
31
|
-
"wireless_mutation": [r"\bset\s+[A-Za-z0-9_ -]+?\s+ssid\b", r"\bsecurity\s+(?:wpa|wpa2|wep|open|802\.1x)\b", r"\bpassphrase\b", r"\bchannel\s+\d+\b"],
|
|
31
|
+
"wireless_mutation": [r"\bset\s+[A-Za-z0-9_ -]+?\s+ssid\b", r"\bsecurity\s+(?:wpa|wpa2|wpa-enterprise|wpa2-enterprise|wep|open|802\.1x)\b", r"\bpassphrase\b", r"\bchannel\s+\d+\b"],
|
|
32
32
|
"wireless_client": [r"\btablet\b", r"\blaptop\b", r"\bsmartphone\b", r"\bassociate\b", r"\bjoin\b"],
|
|
33
33
|
"wireless_client_association": [r"\bassociate\b", r"\bjoin\b"],
|
|
34
34
|
"tablet": [r"\btablet\b"],
|
|
@@ -38,13 +38,75 @@ CAPABILITY_PATTERNS = {
|
|
|
38
38
|
"ospf": [r"\bospf\b"],
|
|
39
39
|
"eigrp": [r"\beigrp\b"],
|
|
40
40
|
"rip": [r"\brip\b"],
|
|
41
|
+
"ipv6_slaac": [r"\bslaac\b", r"\bipv6\s+slaac\b"],
|
|
42
|
+
"dhcpv6_stateful": [r"\bdhcpv6\b", r"\bstateful dhcpv6\b", r"\bipv6 stateful\b"],
|
|
43
|
+
"dhcpv6_stateless": [r"\bstateless dhcpv6\b", r"\bipv6 stateless\b"],
|
|
44
|
+
"ipv6_prefix_delegation": [r"\bprefix delegation\b", r"\bipv6 prefix\b"],
|
|
45
|
+
"ipv6_dns_aaaa": [r"\baaaa\b", r"\bipv6 dns\b"],
|
|
46
|
+
"ipv6_tunneling": [r"\bipv6ip\b", r"\bipv6 tunnel\b", r"\bipv6 tunneling\b"],
|
|
47
|
+
"isatap": [r"\bisatap\b"],
|
|
48
|
+
"ospfv3": [r"\bospfv3\b", r"\bipv6 ospf\b"],
|
|
49
|
+
"eigrp_ipv6": [r"\bipv6 eigrp\b", r"\beigrp ipv6\b"],
|
|
50
|
+
"ripng": [r"\bripng\b", r"\bipv6 rip\b"],
|
|
51
|
+
"hsrp": [r"\bhsrp\b"],
|
|
41
52
|
"nat": [r"\bnat\b"],
|
|
42
53
|
"acl": [r"\bacl\b", r"\baccess-list\b"],
|
|
54
|
+
"dhcp_snooping": [r"\bdhcp snooping\b"],
|
|
55
|
+
"dai": [r"\bdai\b", r"\bdynamic arp inspection\b"],
|
|
56
|
+
"dot1x": [r"\bdot1x\b", r"\b802\.1x\b", r"\bnac\b"],
|
|
57
|
+
"lldp": [r"\blldp\b"],
|
|
58
|
+
"rep": [r"\brep\b"],
|
|
59
|
+
"snmp": [r"\bsnmp\b"],
|
|
60
|
+
"netflow": [r"\bnetflow\b"],
|
|
61
|
+
"span": [r"\bspan\b", r"\brspan\b"],
|
|
62
|
+
"qos": [r"\bqos policy\b", r"\bqos class\b", r"\bclass-map\b", r"\bpolicy-map\b", r"\bquality of service\b"],
|
|
63
|
+
"port_security": [r"\bport security\b", r"\bport-security\b"],
|
|
43
64
|
"vpn": [r"\bvpn\b", r"\bsite-to-site\b", r"\bipsec\b", r"\btunnel\b"],
|
|
44
65
|
"ipsec": [r"\bipsec\b", r"\bike\b", r"\bphase 1\b", r"\bphase 2\b"],
|
|
45
66
|
"gre": [r"\bgre\b", r"\bgre tunnel\b"],
|
|
46
67
|
"ppp": [r"\bppp\b", r"\bchap\b", r"\bpap\b"],
|
|
68
|
+
"security_edge": [r"\bsecurity edge\b", r"\bsecurity appliance\b", r"\bfirewall\b", r"\basa\b"],
|
|
69
|
+
"asa_acl_nat": [r"\basa acl\b", r"\basa nat\b", r"\basa acl nat\b"],
|
|
70
|
+
"asa_service_policy": [r"\basa service policy\b", r"\bservice policy\b"],
|
|
71
|
+
"clientless_vpn": [r"\bclientless vpn\b"],
|
|
72
|
+
"cbac": [r"\bcbac\b"],
|
|
73
|
+
"zfw": [r"\bzfw\b", r"\bzone based firewall\b", r"\bzone-based firewall\b"],
|
|
74
|
+
"sniffer": [r"\bsniffer\b"],
|
|
47
75
|
"multilayer_switching": [r"\bmultilayer switch\b", r"\blayer 3 switch\b", r"\b3560\b", r"\bsvi\b"],
|
|
76
|
+
"wlc": [r"\bwlc\b", r"\bwireless lan controller\b"],
|
|
77
|
+
"wpa_enterprise": [r"\bwpa enterprise\b", r"\bwpa2 enterprise\b", r"\bwpa-enterprise\b", r"\bwpa2-enterprise\b", r"\b802\.1x wireless\b"],
|
|
78
|
+
"wep": [r"\bwep\b"],
|
|
79
|
+
"guest_wifi": [r"\bguest wifi\b", r"\bguest wlan\b"],
|
|
80
|
+
"beamforming": [r"\bbeamforming\b"],
|
|
81
|
+
"meraki": [r"\bmeraki\b"],
|
|
82
|
+
"cellular_5g": [r"\b5g\b", r"\bcellular\b", r"\bcell tower\b"],
|
|
83
|
+
"bluetooth": [r"\bbluetooth\b"],
|
|
84
|
+
"network_controller": [r"\bnetwork controller\b", r"\bnetcon\b"],
|
|
85
|
+
"python_programming": [r"\bpython programming\b", r"\bpython app\b"],
|
|
86
|
+
"javascript_programming": [r"\bjavascript\b", r"\bjs app\b"],
|
|
87
|
+
"blockly_programming": [r"\bblockly\b"],
|
|
88
|
+
"tcp_udp_app": [r"\btcp test app\b", r"\budp test app\b", r"\btcp app\b", r"\budp app\b"],
|
|
89
|
+
"vm_iox": [r"\biox\b", r"\bvm\b", r"\bvirtual machine\b"],
|
|
90
|
+
"voip": [r"\bvoip\b"],
|
|
91
|
+
"ip_phone": [r"\bip phone\b", r"\bphone\b"],
|
|
92
|
+
"call_manager": [r"\bcall manager\b", r"\btelephony\b"],
|
|
93
|
+
"linksys_voice": [r"\blinksys voice\b"],
|
|
94
|
+
"mqtt": [r"\bmqtt\b"],
|
|
95
|
+
"real_http": [r"\breal http\b"],
|
|
96
|
+
"real_websocket": [r"\bwebsocket\b", r"\breal websocket\b"],
|
|
97
|
+
"visual_scripting": [r"\bvisual scripting\b"],
|
|
98
|
+
"ptp": [r"\bptp\b", r"\bprecision time protocol\b"],
|
|
99
|
+
"profinet": [r"\bprofinet\b"],
|
|
100
|
+
"l2nat": [r"\bl2nat\b", r"\bl2 nat\b"],
|
|
101
|
+
"cyberobserver": [r"\bcyberobserver\b"],
|
|
102
|
+
"industrial_firewall": [r"\bindustrial firewall\b", r"\bisa3000\b"],
|
|
103
|
+
"coaxial": [r"\bcoaxial\b"],
|
|
104
|
+
"cable_dsl": [r"\bcable modem\b", r"\bdsl\b", r"\bdsl modem\b"],
|
|
105
|
+
"central_office": [r"\bcentral office\b"],
|
|
106
|
+
"cell_tower": [r"\bcell tower\b"],
|
|
107
|
+
"power_distribution": [r"\bpower distribution\b"],
|
|
108
|
+
"hot_swappable": [r"\bhot swappable\b", r"\bhot-swappable\b"],
|
|
109
|
+
"ios_license": [r"\bios license\b", r"\blicense\b", r"\bios15\b", r"\bios 15\b"],
|
|
48
110
|
}
|
|
49
111
|
|
|
50
112
|
DEVICE_SYNONYMS = {
|
|
@@ -97,6 +159,13 @@ NATURAL_DEVICE_ALIASES = {
|
|
|
97
159
|
NETWORK_STYLE_PATTERNS = {
|
|
98
160
|
"campus": [r"\bcampus\b", r"\bkampus\b", r"\bsobeli\b", r"\bdepart", r"\bdepartment\b"],
|
|
99
161
|
"branch": [r"\bbranch\b", r"\bfilial\b"],
|
|
162
|
+
"ipv6_routing": [r"\bipv6\b", r"\bslaac\b", r"\bdhcpv6\b", r"\bospfv3\b", r"\bhsrp\b"],
|
|
163
|
+
"l2_security_monitoring": [r"\bdhcp snooping\b", r"\bdai\b", r"\bdot1x\b", r"\b802\.1x\b", r"\bsnmp\b", r"\bnetflow\b", r"\bspan\b", r"\brspan\b", r"\bquality of service\b", r"\blldp\b", r"\brep\b", r"\bport security\b", r"\bport-security\b"],
|
|
164
|
+
"wireless_advanced": [r"\bwlc\b", r"\bwireless lan controller\b", r"\bmeraki\b", r"\bbluetooth\b", r"\b5g\b", r"\bcellular\b", r"\bwpa enterprise\b", r"\bwpa2 enterprise\b", r"\bwep\b", r"\bguest wifi\b", r"\bguest wlan\b", r"\bbeamforming\b"],
|
|
165
|
+
"automation_controller": [r"\bnetwork controller\b", r"\bnetcon\b", r"\bblockly\b", r"\bjavascript\b", r"\bpython programming\b", r"\biox\b"],
|
|
166
|
+
"voice_collaboration": [r"\bvoip\b", r"\bip phone\b", r"\bcall manager\b", r"\btelephony\b"],
|
|
167
|
+
"industrial_iot": [r"\bmqtt\b", r"\bwebsocket\b", r"\bprofinet\b", r"\bl2nat\b", r"\bptp\b", r"\bcyberobserver\b", r"\bindustrial\b"],
|
|
168
|
+
"physical_media_device": [r"\bcoaxial\b", r"\bcable modem\b", r"\bdsl modem\b", r"\bcentral office\b", r"\bcell tower\b", r"\bpower distribution\b"],
|
|
100
169
|
"small_office": [r"\bsmall office\b", r"\bhome\b", r"\bofis\b"],
|
|
101
170
|
"wireless_branch": [r"\bwireless\b", r"\bwifi\b", r"\bssid\b"],
|
|
102
171
|
"wan_security": [r"\bwan\b", r"\bgre\b", r"\bppp\b", r"\bipsec\b", r"\bvpn\b", r"\bsecurity edge\b", r"\bfirewall\b", r"\basa\b"],
|
|
@@ -136,6 +205,8 @@ SECURITY_TO_AUTH = {
|
|
|
136
205
|
"wpa": ("2", "2"),
|
|
137
206
|
"wpa2": ("4", "4"),
|
|
138
207
|
"802.1x": ("5", "5"),
|
|
208
|
+
"wpa-enterprise": ("5", "5"),
|
|
209
|
+
"wpa2-enterprise": ("5", "5"),
|
|
139
210
|
}
|
|
140
211
|
|
|
141
212
|
TRANSLITERATION_TABLE = str.maketrans(
|
|
@@ -389,7 +460,7 @@ def _extract_service_requirements(capabilities: list[str], prompt: str) -> dict[
|
|
|
389
460
|
for service in ["dhcp", "dns", "http", "https", "ftp", "tftp", "ntp", "email", "syslog", "aaa"]:
|
|
390
461
|
if service in lowered:
|
|
391
462
|
requirements["services"].append(service)
|
|
392
|
-
for security in ["acl", "telnet", "wpa2", "wpa", "wep", "nat", "vpn", "ipsec", "gre", "ppp", "ssh"]:
|
|
463
|
+
for security in ["acl", "telnet", "wpa2", "wpa", "wep", "nat", "vpn", "ipsec", "gre", "ppp", "security_edge", "ssh"]:
|
|
393
464
|
if security in lowered:
|
|
394
465
|
requirements["security"].append(security)
|
|
395
466
|
return requirements
|
|
@@ -503,6 +574,39 @@ def _extract_switch_ops(prompt: str) -> list[dict[str, object]]:
|
|
|
503
574
|
"native": int(native) if native else None,
|
|
504
575
|
}
|
|
505
576
|
)
|
|
577
|
+
dhcp_snooping_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+dhcp\s+snooping\s+vlan\s+(\d+)(?:\s+trust\s+([A-Za-z0-9/._-]+))?", flags=re.IGNORECASE)
|
|
578
|
+
dai_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+dai\s+vlan\s+(\d+)(?:\s+trust\s+([A-Za-z0-9/._-]+))?", flags=re.IGNORECASE)
|
|
579
|
+
port_security_pattern = re.compile(
|
|
580
|
+
r"set\s+([A-Za-z0-9_-]+)\s+port-security\s+([A-Za-z0-9/._-]+)(?:\s+max\s+(\d+))?(?:\s+violation\s+(protect|restrict|shutdown))?",
|
|
581
|
+
flags=re.IGNORECASE,
|
|
582
|
+
)
|
|
583
|
+
lldp_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+lldp(?:\s+(?:enable|run))?", flags=re.IGNORECASE)
|
|
584
|
+
rep_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+rep\s+segment\s+(\d+)\s+interface\s+([A-Za-z0-9/._-]+)", flags=re.IGNORECASE)
|
|
585
|
+
span_pattern = re.compile(
|
|
586
|
+
r"set\s+([A-Za-z0-9_-]+)\s+(?:r?span)\s+(\d+)\s+source\s+([A-Za-z0-9/._-]+)\s+destination\s+([A-Za-z0-9/._-]+)",
|
|
587
|
+
flags=re.IGNORECASE,
|
|
588
|
+
)
|
|
589
|
+
for segment in _command_segments(prompt):
|
|
590
|
+
for device, vlan_id, trust_port in dhcp_snooping_pattern.findall(segment):
|
|
591
|
+
ops.append({"op": "set_dhcp_snooping", "device": device, "vlan": int(vlan_id), "trust_port": trust_port or None})
|
|
592
|
+
for device, vlan_id, trust_port in dai_pattern.findall(segment):
|
|
593
|
+
ops.append({"op": "set_dai", "device": device, "vlan": int(vlan_id), "trust_port": trust_port or None})
|
|
594
|
+
for device, port, maximum, violation in port_security_pattern.findall(segment):
|
|
595
|
+
ops.append(
|
|
596
|
+
{
|
|
597
|
+
"op": "set_port_security",
|
|
598
|
+
"device": device,
|
|
599
|
+
"port": port,
|
|
600
|
+
"maximum": int(maximum) if maximum else None,
|
|
601
|
+
"violation": violation.lower() if violation else None,
|
|
602
|
+
}
|
|
603
|
+
)
|
|
604
|
+
for device in lldp_pattern.findall(segment):
|
|
605
|
+
ops.append({"op": "set_lldp", "device": device})
|
|
606
|
+
for device, segment_id, interface_name in rep_pattern.findall(segment):
|
|
607
|
+
ops.append({"op": "set_rep", "device": device, "segment": int(segment_id), "interface": interface_name})
|
|
608
|
+
for device, session, source, destination in span_pattern.findall(segment):
|
|
609
|
+
ops.append({"op": "set_span", "device": device, "session": int(session), "source": source, "destination": destination})
|
|
506
610
|
return ops
|
|
507
611
|
|
|
508
612
|
|
|
@@ -570,6 +674,91 @@ def _extract_router_ops(prompt: str) -> list[dict[str, object]]:
|
|
|
570
674
|
for segment in _command_segments(prompt):
|
|
571
675
|
for acl_name, direction, device, interface_name in acl_apply_pattern.findall(segment):
|
|
572
676
|
ops.append({"op": "apply_acl", "device": device.strip(), "acl_name": acl_name, "direction": direction.lower(), "interface": interface_name})
|
|
677
|
+
ipv6_unicast_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+ipv6\s+unicast-routing", flags=re.IGNORECASE)
|
|
678
|
+
ipv6_address_pattern = re.compile(
|
|
679
|
+
r"set\s+([A-Za-z0-9_-]+)\s+(?:interface\s+)?([A-Za-z0-9/._-]+)\s+ipv6\s+([0-9A-Fa-f:]+)/(\d+)",
|
|
680
|
+
flags=re.IGNORECASE,
|
|
681
|
+
)
|
|
682
|
+
ipv6_slaac_pattern = re.compile(
|
|
683
|
+
r"set\s+([A-Za-z0-9_-]+)\s+slaac\s+on\s+([A-Za-z0-9/._-]+)(?:\s+prefix\s+([0-9A-Fa-f:]+)/(\d+))?",
|
|
684
|
+
flags=re.IGNORECASE,
|
|
685
|
+
)
|
|
686
|
+
dhcpv6_pattern = re.compile(
|
|
687
|
+
r"set\s+([A-Za-z0-9_-]+)\s+dhcpv6\s+pool\s+([A-Za-z0-9_-]+)\s+prefix\s+([0-9A-Fa-f:]+)/(\d+)\s+interface\s+([A-Za-z0-9/._-]+)"
|
|
688
|
+
r"(?:\s+dns\s+([0-9A-Fa-f:]+))?(?:\s+domain\s+([A-Za-z0-9._-]+))?",
|
|
689
|
+
flags=re.IGNORECASE,
|
|
690
|
+
)
|
|
691
|
+
ospfv3_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+ospfv3\s+(\d+)\s+area\s+(\d+)\s+interface\s+([A-Za-z0-9/._-]+)", flags=re.IGNORECASE)
|
|
692
|
+
eigrp_ipv6_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+eigrp\s+ipv6\s+(\d+)\s+interface\s+([A-Za-z0-9/._-]+)", flags=re.IGNORECASE)
|
|
693
|
+
ripng_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+ripng\s+([A-Za-z0-9_-]+)\s+interface\s+([A-Za-z0-9/._-]+)", flags=re.IGNORECASE)
|
|
694
|
+
hsrp_pattern = re.compile(
|
|
695
|
+
r"set\s+([A-Za-z0-9_-]+)\s+hsrp\s+(\d+)\s+ipv6\s+([0-9A-Fa-f:]+)\s+interface\s+([A-Za-z0-9/._-]+)(?:\s+priority\s+(\d+))?",
|
|
696
|
+
flags=re.IGNORECASE,
|
|
697
|
+
)
|
|
698
|
+
snmp_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+snmp\s+community\s+([A-Za-z0-9_-]+)\s+(ro|rw)", flags=re.IGNORECASE)
|
|
699
|
+
netflow_pattern = re.compile(
|
|
700
|
+
r"set\s+([A-Za-z0-9_-]+)\s+netflow\s+destination\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+)(?:\s+version\s+(\d+))?(?:\s+interface\s+([A-Za-z0-9/._-]+)\s+(ingress|egress))?",
|
|
701
|
+
flags=re.IGNORECASE,
|
|
702
|
+
)
|
|
703
|
+
for segment in _command_segments(prompt):
|
|
704
|
+
for device in ipv6_unicast_pattern.findall(segment):
|
|
705
|
+
ops.append({"op": "enable_ipv6_unicast_routing", "device": device})
|
|
706
|
+
for device, interface_name, address, prefix in ipv6_address_pattern.findall(segment):
|
|
707
|
+
ops.append({"op": "set_ipv6_address", "device": device, "interface": interface_name, "address": address, "prefix": int(prefix)})
|
|
708
|
+
for device, interface_name, prefix_address, prefix_len in ipv6_slaac_pattern.findall(segment):
|
|
709
|
+
ops.append(
|
|
710
|
+
{
|
|
711
|
+
"op": "set_ipv6_slaac",
|
|
712
|
+
"device": device,
|
|
713
|
+
"interface": interface_name,
|
|
714
|
+
"prefix": prefix_address or None,
|
|
715
|
+
"prefix_len": int(prefix_len) if prefix_len else None,
|
|
716
|
+
}
|
|
717
|
+
)
|
|
718
|
+
for device, name, prefix_address, prefix_len, interface_name, dns, domain in dhcpv6_pattern.findall(segment):
|
|
719
|
+
ops.append(
|
|
720
|
+
{
|
|
721
|
+
"op": "set_dhcpv6_pool",
|
|
722
|
+
"device": device,
|
|
723
|
+
"name": name,
|
|
724
|
+
"prefix": prefix_address,
|
|
725
|
+
"prefix_len": int(prefix_len),
|
|
726
|
+
"interface": interface_name,
|
|
727
|
+
"dns": dns or None,
|
|
728
|
+
"domain": domain or None,
|
|
729
|
+
}
|
|
730
|
+
)
|
|
731
|
+
for device, process_id, area, interface_name in ospfv3_pattern.findall(segment):
|
|
732
|
+
ops.append({"op": "set_ospfv3_interface", "device": device, "process_id": int(process_id), "area": int(area), "interface": interface_name})
|
|
733
|
+
for device, asn, interface_name in eigrp_ipv6_pattern.findall(segment):
|
|
734
|
+
ops.append({"op": "set_eigrp_ipv6_interface", "device": device, "asn": int(asn), "interface": interface_name})
|
|
735
|
+
for device, process_name, interface_name in ripng_pattern.findall(segment):
|
|
736
|
+
ops.append({"op": "set_ripng_interface", "device": device, "process_name": process_name, "interface": interface_name})
|
|
737
|
+
for device, group, virtual_ipv6, interface_name, priority in hsrp_pattern.findall(segment):
|
|
738
|
+
ops.append(
|
|
739
|
+
{
|
|
740
|
+
"op": "set_hsrp_ipv6",
|
|
741
|
+
"device": device,
|
|
742
|
+
"group": int(group),
|
|
743
|
+
"virtual_ipv6": virtual_ipv6,
|
|
744
|
+
"interface": interface_name,
|
|
745
|
+
"priority": int(priority) if priority else None,
|
|
746
|
+
}
|
|
747
|
+
)
|
|
748
|
+
for device, community, mode in snmp_pattern.findall(segment):
|
|
749
|
+
ops.append({"op": "set_snmp_community", "device": device, "community": community, "mode": mode.lower()})
|
|
750
|
+
for device, destination, port, version, interface_name, direction in netflow_pattern.findall(segment):
|
|
751
|
+
ops.append(
|
|
752
|
+
{
|
|
753
|
+
"op": "set_netflow",
|
|
754
|
+
"device": device,
|
|
755
|
+
"destination": destination,
|
|
756
|
+
"port": int(port),
|
|
757
|
+
"version": int(version) if version else 9,
|
|
758
|
+
"interface": interface_name or None,
|
|
759
|
+
"direction": direction.lower() if direction else None,
|
|
760
|
+
}
|
|
761
|
+
)
|
|
573
762
|
return ops
|
|
574
763
|
|
|
575
764
|
|
|
@@ -630,14 +819,31 @@ def _extract_management_ops(prompt: str) -> list[dict[str, object]]:
|
|
|
630
819
|
def _extract_wireless_ops(prompt: str) -> list[dict[str, object]]:
|
|
631
820
|
ops: list[dict[str, object]] = []
|
|
632
821
|
ssid_pattern = re.compile(
|
|
633
|
-
r"set\s+([A-Za-z0-9_ -]+?)\s+ssid\s+([A-Za-z0-9._-]+)
|
|
822
|
+
r"set\s+([A-Za-z0-9_ -]+?)\s+ssid\s+([A-Za-z0-9._-]+)"
|
|
823
|
+
r"(?:\s+security\s+(wpa2?\s+enterprise|wpa2?-enterprise|wpa2?-psk|802\.1x|wep|open|wpa2?|wpa))?"
|
|
824
|
+
r"(?:\s+passphrase\s+([A-Za-z0-9._-]+))?"
|
|
825
|
+
r"(?:\s+radius\s+(\d+\.\d+\.\d+\.\d+)\s+secret\s+([A-Za-z0-9._-]+))?"
|
|
826
|
+
r"(?:\s+channel\s+(\d+))?",
|
|
634
827
|
flags=re.IGNORECASE,
|
|
635
828
|
)
|
|
636
829
|
for segment in _command_segments(prompt):
|
|
637
|
-
for device, ssid, security, passphrase, channel in ssid_pattern.findall(segment):
|
|
638
|
-
security_key = (security or "open").lower()
|
|
830
|
+
for device, ssid, security, passphrase, radius_server, radius_secret, channel in ssid_pattern.findall(segment):
|
|
831
|
+
security_key = (security or "open").lower().replace(" ", "-")
|
|
639
832
|
auth_type, encrypt_type = SECURITY_TO_AUTH.get(security_key, ("0", "0"))
|
|
640
|
-
|
|
833
|
+
operation = {
|
|
834
|
+
"op": "set_wireless_ssid",
|
|
835
|
+
"device": device.strip(),
|
|
836
|
+
"ssid": ssid,
|
|
837
|
+
"security": security_key,
|
|
838
|
+
"auth_type": auth_type,
|
|
839
|
+
"encrypt_type": encrypt_type,
|
|
840
|
+
"passphrase": passphrase or "",
|
|
841
|
+
"channel": int(channel) if channel else 1,
|
|
842
|
+
}
|
|
843
|
+
if radius_server:
|
|
844
|
+
operation["radius_server"] = radius_server
|
|
845
|
+
operation["radius_secret"] = radius_secret or ""
|
|
846
|
+
ops.append(operation)
|
|
641
847
|
assoc_pattern = re.compile(r"associate\s+([A-Za-z0-9_ -]+?)\s+to\s+([A-Za-z0-9_ -]+?)\s+ssid\s+([A-Za-z0-9._-]+)(?:\s+(dhcp|static))?", flags=re.IGNORECASE)
|
|
642
848
|
for segment in _command_segments(prompt):
|
|
643
849
|
for client, ap, ssid, ip_mode in assoc_pattern.findall(segment):
|
|
@@ -739,6 +945,22 @@ def parse_intent(prompt: str) -> IntentPlan:
|
|
|
739
945
|
host_link_intent = _extract_host_link_intent(normalized_prompt)
|
|
740
946
|
host_vlan_assignment = _extract_host_vlan_assignment(normalized_prompt)
|
|
741
947
|
network_style = _extract_network_style(normalized_prompt)
|
|
948
|
+
if (
|
|
949
|
+
network_style == "wireless_advanced"
|
|
950
|
+
and "server_aaa" in capability_set
|
|
951
|
+
and re.search(r"\bradius\b", lowered)
|
|
952
|
+
and not re.search(r"\b(?:aaa|acs|server aaa|aaa server)\b", lowered)
|
|
953
|
+
):
|
|
954
|
+
capability_set.discard("server_aaa")
|
|
955
|
+
if re.search(r"\bqos\b", lowered) and (
|
|
956
|
+
re.search(r"\b(?:snmp|netflow|span|rspan|lldp|rep|dai|dot1x|802\.1x|dhcp snooping|port-security|port security)\b", lowered)
|
|
957
|
+
or network_style == "l2_security_monitoring"
|
|
958
|
+
):
|
|
959
|
+
capability_set.add("qos")
|
|
960
|
+
if "dhcp_snooping" in capability_set and not re.search(r"\b(?:dhcp\s+pool|server\s+dhcp|dhcp\s+server|default-router)\b", lowered):
|
|
961
|
+
capability_set.discard("dhcp_pool")
|
|
962
|
+
capability_set.discard("router_dhcp")
|
|
963
|
+
capability_set.discard("server_dhcp")
|
|
742
964
|
department_count = _extract_department_count(normalized_prompt)
|
|
743
965
|
per_department_devices = _extract_per_department_devices(normalized_prompt)
|
|
744
966
|
|
|
@@ -793,12 +1015,61 @@ def parse_intent(prompt: str) -> IntentPlan:
|
|
|
793
1015
|
capability_set.update({"wireless_client", "wireless_client_association"})
|
|
794
1016
|
if any(op["op"] == "set_wireless_ssid" for op in wireless_ops):
|
|
795
1017
|
capability_set.update({"wireless_ap", "wireless_mutation"})
|
|
1018
|
+
for op in wireless_ops:
|
|
1019
|
+
if op.get("op") != "set_wireless_ssid":
|
|
1020
|
+
continue
|
|
1021
|
+
security = str(op.get("security") or "")
|
|
1022
|
+
if security == "wep":
|
|
1023
|
+
capability_set.add("wep")
|
|
1024
|
+
if security in {"wpa-enterprise", "wpa2-enterprise", "802.1x"} or op.get("radius_server"):
|
|
1025
|
+
capability_set.add("wpa_enterprise")
|
|
1026
|
+
elif network_style == "wireless_advanced":
|
|
1027
|
+
capability_set.discard("wireless_ap")
|
|
1028
|
+
capability_set.discard("wireless_mutation")
|
|
796
1029
|
if end_device_ops:
|
|
797
1030
|
capability_set.add("end_device_mutation")
|
|
798
1031
|
if iot_ops:
|
|
799
1032
|
capability_set.update({"iot", "iot_registration"})
|
|
800
|
-
|
|
1033
|
+
switch_op_names = {str(op.get("op")) for op in switch_ops}
|
|
1034
|
+
if "set_dhcp_snooping" in switch_op_names:
|
|
1035
|
+
capability_set.add("dhcp_snooping")
|
|
1036
|
+
if "set_dai" in switch_op_names:
|
|
1037
|
+
capability_set.add("dai")
|
|
1038
|
+
if "set_lldp" in switch_op_names:
|
|
1039
|
+
capability_set.add("lldp")
|
|
1040
|
+
if "set_rep" in switch_op_names:
|
|
1041
|
+
capability_set.add("rep")
|
|
1042
|
+
if "set_span" in switch_op_names:
|
|
1043
|
+
capability_set.add("span")
|
|
1044
|
+
if "set_port_security" in switch_op_names:
|
|
1045
|
+
capability_set.add("port_security")
|
|
1046
|
+
router_op_names = {str(op.get("op")) for op in router_ops}
|
|
1047
|
+
if "set_snmp_community" in router_op_names:
|
|
1048
|
+
capability_set.add("snmp")
|
|
1049
|
+
if "set_netflow" in router_op_names:
|
|
1050
|
+
capability_set.add("netflow")
|
|
1051
|
+
if router_op_names & {"enable_ipv6_unicast_routing", "set_ipv6_address", "set_ipv6_slaac"}:
|
|
1052
|
+
capability_set.add("ipv6_slaac")
|
|
1053
|
+
if "set_dhcpv6_pool" in router_op_names:
|
|
1054
|
+
capability_set.add("dhcpv6_stateful")
|
|
1055
|
+
if "set_ospfv3_interface" in router_op_names:
|
|
1056
|
+
capability_set.add("ospfv3")
|
|
1057
|
+
if "set_eigrp_ipv6_interface" in router_op_names:
|
|
1058
|
+
capability_set.add("eigrp_ipv6")
|
|
1059
|
+
if "set_ripng_interface" in router_op_names:
|
|
1060
|
+
capability_set.add("ripng")
|
|
1061
|
+
if "set_hsrp_ipv6" in router_op_names:
|
|
1062
|
+
capability_set.add("hsrp")
|
|
1063
|
+
if capability_set & {"vpn", "ipsec", "gre", "ppp", "security_edge"}:
|
|
801
1064
|
network_style = network_style or "wan_security"
|
|
1065
|
+
if capability_set & {"ipv6_slaac", "dhcpv6_stateful", "dhcpv6_stateless", "ospfv3", "eigrp_ipv6", "ripng", "hsrp"}:
|
|
1066
|
+
network_style = network_style or "ipv6_routing"
|
|
1067
|
+
if capability_set & {"dhcp_snooping", "dai", "dot1x", "lldp", "rep", "snmp", "netflow", "span", "qos", "port_security"}:
|
|
1068
|
+
network_style = network_style or "l2_security_monitoring"
|
|
1069
|
+
if capability_set & {"wlc", "wpa_enterprise", "wep", "guest_wifi", "beamforming", "meraki", "cellular_5g", "bluetooth"}:
|
|
1070
|
+
network_style = "wireless_advanced"
|
|
1071
|
+
if capability_set & {"mqtt", "real_http", "real_websocket", "visual_scripting", "ptp", "profinet", "l2nat", "cyberobserver", "industrial_firewall"}:
|
|
1072
|
+
network_style = "industrial_iot"
|
|
802
1073
|
|
|
803
1074
|
capabilities = sorted(capability_set)
|
|
804
1075
|
|
package/scripts/pkt_editor.py
CHANGED
|
@@ -277,6 +277,64 @@ def inventory_management(root: ET.Element) -> dict[str, dict[str, object]]:
|
|
|
277
277
|
return result
|
|
278
278
|
|
|
279
279
|
|
|
280
|
+
def inventory_routing(root: ET.Element) -> dict[str, dict[str, object]]:
|
|
281
|
+
result: dict[str, dict[str, object]] = {}
|
|
282
|
+
for device in root.findall(".//DEVICES/DEVICE"):
|
|
283
|
+
name = device.findtext("./ENGINE/NAME", default="")
|
|
284
|
+
running = "\n".join(line.text or "" for line in device.findall("./ENGINE/RUNNINGCONFIG/LINE"))
|
|
285
|
+
if not running:
|
|
286
|
+
continue
|
|
287
|
+
capabilities: set[str] = set()
|
|
288
|
+
if re.search(r"(?mi)^\s*ipv6\s+unicast-routing\s*$", running) or re.search(r"(?mi)^\s*ipv6\s+(?:enable|address)\b", running):
|
|
289
|
+
capabilities.add("ipv6_slaac")
|
|
290
|
+
if re.search(r"(?mi)^\s*ipv6\s+dhcp\s+pool\b", running) or re.search(r"(?mi)^\s*ipv6\s+dhcp\s+server\b", running):
|
|
291
|
+
capabilities.add("dhcpv6_stateful")
|
|
292
|
+
if re.search(r"(?mi)^\s*ipv6\s+ospf\b", running) or re.search(r"(?mi)^\s*ipv6\s+router\s+ospf\b", running):
|
|
293
|
+
capabilities.add("ospfv3")
|
|
294
|
+
if re.search(r"(?mi)^\s*ipv6\s+eigrp\b", running) or re.search(r"(?mi)^\s*ipv6\s+router\s+eigrp\b", running):
|
|
295
|
+
capabilities.add("eigrp_ipv6")
|
|
296
|
+
if re.search(r"(?mi)^\s*ipv6\s+rip\b", running):
|
|
297
|
+
capabilities.add("ripng")
|
|
298
|
+
if re.search(r"(?mi)^\s*standby\s+\d+\s+ipv6\b", running):
|
|
299
|
+
capabilities.add("hsrp")
|
|
300
|
+
if capabilities:
|
|
301
|
+
result[name] = {"capabilities": sorted(capabilities)}
|
|
302
|
+
return result
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def inventory_l2_security_monitoring(root: ET.Element) -> dict[str, dict[str, object]]:
|
|
306
|
+
result: dict[str, dict[str, object]] = {}
|
|
307
|
+
for device in root.findall(".//DEVICES/DEVICE"):
|
|
308
|
+
name = device.findtext("./ENGINE/NAME", default="")
|
|
309
|
+
running = "\n".join(line.text or "" for line in device.findall("./ENGINE/RUNNINGCONFIG/LINE"))
|
|
310
|
+
if not running:
|
|
311
|
+
continue
|
|
312
|
+
capabilities: set[str] = set()
|
|
313
|
+
if re.search(r"(?mi)^\s*ip\s+dhcp\s+snooping\b", running):
|
|
314
|
+
capabilities.add("dhcp_snooping")
|
|
315
|
+
if re.search(r"(?mi)^\s*ip\s+arp\s+inspection\b", running):
|
|
316
|
+
capabilities.add("dai")
|
|
317
|
+
if re.search(r"(?mi)^\s*(?:aaa\s+new-model|radius-server\s+host|authentication\s+port-control|dot1x\b)", running):
|
|
318
|
+
capabilities.add("dot1x")
|
|
319
|
+
if re.search(r"(?mi)^\s*lldp\s+run\b", running):
|
|
320
|
+
capabilities.add("lldp")
|
|
321
|
+
if re.search(r"(?mi)^\s*rep\s+segment\b", running):
|
|
322
|
+
capabilities.add("rep")
|
|
323
|
+
if re.search(r"(?mi)^\s*snmp-server\b", running):
|
|
324
|
+
capabilities.add("snmp")
|
|
325
|
+
if re.search(r"(?mi)^\s*ip\s+flow-export\b", running) or re.search(r"(?mi)^\s*ip\s+flow\s+(?:ingress|egress)\b", running):
|
|
326
|
+
capabilities.add("netflow")
|
|
327
|
+
if re.search(r"(?mi)^\s*monitor\s+session\b", running):
|
|
328
|
+
capabilities.add("span")
|
|
329
|
+
if re.search(r"(?mi)^\s*(?:mls\s+qos|auto\s+qos|class-map|policy-map|service-policy)\b", running):
|
|
330
|
+
capabilities.add("qos")
|
|
331
|
+
if re.search(r"(?mi)^\s*switchport\s+port-security\b", running):
|
|
332
|
+
capabilities.add("port_security")
|
|
333
|
+
if capabilities:
|
|
334
|
+
result[name] = {"capabilities": sorted(capabilities)}
|
|
335
|
+
return result
|
|
336
|
+
|
|
337
|
+
|
|
280
338
|
def inventory_topology_summary(root: ET.Element) -> dict[str, object]:
|
|
281
339
|
devices = inventory_devices(root)
|
|
282
340
|
counts: dict[str, int] = {}
|
|
@@ -303,6 +361,8 @@ def inventory_root(root: ET.Element) -> dict[str, object]:
|
|
|
303
361
|
"dhcp_pools": inventory_dhcp_pools(root),
|
|
304
362
|
"acl_names": inventory_acl_names(root),
|
|
305
363
|
"management": inventory_management(root),
|
|
364
|
+
"routing": inventory_routing(root),
|
|
365
|
+
"l2_security_monitoring": inventory_l2_security_monitoring(root),
|
|
306
366
|
"topology_summary": inventory_topology_summary(root),
|
|
307
367
|
}
|
|
308
368
|
|
|
@@ -702,6 +762,45 @@ def _apply_switch_op(device: ET.Element, operation: dict[str, object]) -> None:
|
|
|
702
762
|
for target in _config_targets(device):
|
|
703
763
|
_append_config_block(target, f"interface {operation['port']}", body)
|
|
704
764
|
return
|
|
765
|
+
elif operation["op"] == "set_dhcp_snooping":
|
|
766
|
+
for target in _config_targets(device):
|
|
767
|
+
_append_unique_config_lines(target, ["ip dhcp snooping", f"ip dhcp snooping vlan {operation['vlan']}"])
|
|
768
|
+
if operation.get("trust_port"):
|
|
769
|
+
_append_config_block(target, f"interface {operation['trust_port']}", [" ip dhcp snooping trust"])
|
|
770
|
+
return
|
|
771
|
+
elif operation["op"] == "set_dai":
|
|
772
|
+
for target in _config_targets(device):
|
|
773
|
+
_append_unique_config_lines(target, ["ip dhcp snooping", f"ip dhcp snooping vlan {operation['vlan']}", f"ip arp inspection vlan {operation['vlan']}"])
|
|
774
|
+
if operation.get("trust_port"):
|
|
775
|
+
_append_config_block(target, f"interface {operation['trust_port']}", [" ip arp inspection trust", " ip dhcp snooping trust"])
|
|
776
|
+
return
|
|
777
|
+
elif operation["op"] == "set_port_security":
|
|
778
|
+
body = [" switchport mode access", " switchport port-security"]
|
|
779
|
+
if operation.get("maximum"):
|
|
780
|
+
body.append(f" switchport port-security maximum {operation['maximum']}")
|
|
781
|
+
if operation.get("violation"):
|
|
782
|
+
body.append(f" switchport port-security violation {operation['violation']}")
|
|
783
|
+
for target in _config_targets(device):
|
|
784
|
+
_append_config_block(target, f"interface {operation['port']}", body)
|
|
785
|
+
return
|
|
786
|
+
elif operation["op"] == "set_lldp":
|
|
787
|
+
for target in _config_targets(device):
|
|
788
|
+
_append_unique_config_lines(target, ["lldp run"])
|
|
789
|
+
return
|
|
790
|
+
elif operation["op"] == "set_rep":
|
|
791
|
+
for target in _config_targets(device):
|
|
792
|
+
_append_config_block(target, f"interface {operation['interface']}", [f" rep segment {operation['segment']}"])
|
|
793
|
+
return
|
|
794
|
+
elif operation["op"] == "set_span":
|
|
795
|
+
for target in _config_targets(device):
|
|
796
|
+
_append_unique_config_lines(
|
|
797
|
+
target,
|
|
798
|
+
[
|
|
799
|
+
f"monitor session {operation['session']} source interface {operation['source']}",
|
|
800
|
+
f"monitor session {operation['session']} destination interface {operation['destination']}",
|
|
801
|
+
],
|
|
802
|
+
)
|
|
803
|
+
return
|
|
705
804
|
else:
|
|
706
805
|
return
|
|
707
806
|
|
|
@@ -742,6 +841,81 @@ def _apply_router_op(device: ET.Element, operation: dict[str, object]) -> None:
|
|
|
742
841
|
for target in _config_targets(device):
|
|
743
842
|
_append_config_block(target, f"interface {operation['interface']}", [f" ip access-group {operation['acl_name']} {operation['direction']}"])
|
|
744
843
|
return
|
|
844
|
+
elif operation["op"] == "enable_ipv6_unicast_routing":
|
|
845
|
+
for target in _config_targets(device):
|
|
846
|
+
_append_unique_config_lines(target, ["ipv6 unicast-routing"])
|
|
847
|
+
return
|
|
848
|
+
elif operation["op"] == "set_ipv6_address":
|
|
849
|
+
for target in _config_targets(device):
|
|
850
|
+
_append_unique_config_lines(target, ["ipv6 unicast-routing"])
|
|
851
|
+
_append_config_block(
|
|
852
|
+
target,
|
|
853
|
+
f"interface {operation['interface']}",
|
|
854
|
+
[f" ipv6 address {operation['address']}/{operation['prefix']}", " no shutdown"],
|
|
855
|
+
)
|
|
856
|
+
return
|
|
857
|
+
elif operation["op"] == "set_ipv6_slaac":
|
|
858
|
+
body = [" ipv6 enable", " no shutdown"]
|
|
859
|
+
if operation.get("prefix") and operation.get("prefix_len"):
|
|
860
|
+
body.insert(1, f" ipv6 nd prefix {operation['prefix']}/{operation['prefix_len']}")
|
|
861
|
+
for target in _config_targets(device):
|
|
862
|
+
_append_unique_config_lines(target, ["ipv6 unicast-routing"])
|
|
863
|
+
_append_config_block(target, f"interface {operation['interface']}", body)
|
|
864
|
+
return
|
|
865
|
+
elif operation["op"] == "set_dhcpv6_pool":
|
|
866
|
+
pool_body = [f" address prefix {operation['prefix']}/{operation['prefix_len']}"]
|
|
867
|
+
if operation.get("dns"):
|
|
868
|
+
pool_body.append(f" dns-server {operation['dns']}")
|
|
869
|
+
if operation.get("domain"):
|
|
870
|
+
pool_body.append(f" domain-name {operation['domain']}")
|
|
871
|
+
interface_body = [f" ipv6 dhcp server {operation['name']}", " ipv6 nd managed-config-flag", " no shutdown"]
|
|
872
|
+
for target in _config_targets(device):
|
|
873
|
+
_append_unique_config_lines(target, ["ipv6 unicast-routing"])
|
|
874
|
+
_append_config_block(target, f"ipv6 dhcp pool {operation['name']}", pool_body)
|
|
875
|
+
_append_config_block(target, f"interface {operation['interface']}", interface_body)
|
|
876
|
+
return
|
|
877
|
+
elif operation["op"] == "set_ospfv3_interface":
|
|
878
|
+
process_id = operation["process_id"]
|
|
879
|
+
for target in _config_targets(device):
|
|
880
|
+
_append_unique_config_lines(target, ["ipv6 unicast-routing"])
|
|
881
|
+
_append_config_block(target, f"interface {operation['interface']}", [f" ipv6 ospf {process_id} area {operation['area']}", " no shutdown"])
|
|
882
|
+
_append_config_block(target, f"ipv6 router ospf {process_id}", [])
|
|
883
|
+
return
|
|
884
|
+
elif operation["op"] == "set_eigrp_ipv6_interface":
|
|
885
|
+
asn = operation["asn"]
|
|
886
|
+
for target in _config_targets(device):
|
|
887
|
+
_append_unique_config_lines(target, ["ipv6 unicast-routing", "no ipv6 cef"])
|
|
888
|
+
_append_config_block(target, f"interface {operation['interface']}", [f" ipv6 eigrp {asn}", " no shutdown"])
|
|
889
|
+
_append_config_block(target, f"ipv6 router eigrp {asn}", [" no shutdown"])
|
|
890
|
+
return
|
|
891
|
+
elif operation["op"] == "set_ripng_interface":
|
|
892
|
+
for target in _config_targets(device):
|
|
893
|
+
_append_unique_config_lines(target, ["ipv6 unicast-routing"])
|
|
894
|
+
_append_config_block(target, f"interface {operation['interface']}", [f" ipv6 rip {operation['process_name']} enable", " no shutdown"])
|
|
895
|
+
return
|
|
896
|
+
elif operation["op"] == "set_hsrp_ipv6":
|
|
897
|
+
body = [" standby version 2", f" standby {operation['group']} ipv6 {operation['virtual_ipv6']}"]
|
|
898
|
+
if operation.get("priority"):
|
|
899
|
+
body.append(f" standby {operation['group']} priority {operation['priority']}")
|
|
900
|
+
body.append(f" standby {operation['group']} preempt")
|
|
901
|
+
for target in _config_targets(device):
|
|
902
|
+
_append_unique_config_lines(target, ["ipv6 unicast-routing"])
|
|
903
|
+
_append_config_block(target, f"interface {operation['interface']}", body)
|
|
904
|
+
return
|
|
905
|
+
elif operation["op"] == "set_snmp_community":
|
|
906
|
+
for target in _config_targets(device):
|
|
907
|
+
_append_unique_config_lines(target, [f"snmp-server community {operation['community']} {operation['mode']}"])
|
|
908
|
+
return
|
|
909
|
+
elif operation["op"] == "set_netflow":
|
|
910
|
+
global_lines = [
|
|
911
|
+
f"ip flow-export destination {operation['destination']} {operation['port']}",
|
|
912
|
+
f"ip flow-export version {operation['version']}",
|
|
913
|
+
]
|
|
914
|
+
for target in _config_targets(device):
|
|
915
|
+
_append_unique_config_lines(target, global_lines)
|
|
916
|
+
if operation.get("interface") and operation.get("direction"):
|
|
917
|
+
_append_config_block(target, f"interface {operation['interface']}", [f" ip flow {operation['direction']}"])
|
|
918
|
+
return
|
|
745
919
|
else:
|
|
746
920
|
return
|
|
747
921
|
for target in _config_targets(device):
|