packet-tracer-skill 0.2.1 → 0.2.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/CHANGELOG.md +53 -2
- package/README.md +443 -124
- package/docs/automation-controller-proof.md +35 -0
- package/docs/campus-donor-proof.md +103 -0
- package/docs/curated-donor-registry.md +37 -0
- package/docs/generate-ready-pilot-design.md +30 -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/home-iot-donor-proof.md +64 -0
- package/docs/industrial-programming-proof.md +48 -0
- package/docs/ipv4-routing-management-proof.md +37 -0
- package/docs/l2-resiliency-bgp-proof.md +60 -0
- package/docs/l2-security-qos-proof.md +59 -0
- package/docs/launch-announcement-0.2.2.md +15 -0
- package/docs/packet-tracer-feature-gap-atlas.md +244 -0
- package/docs/post-launch-follow-up.md +39 -0
- package/docs/publish-preview-roadmap.md +20 -17
- package/docs/release-checklist.md +32 -17
- package/docs/release-notes-0.2.1.md +7 -6
- package/docs/release-notes-0.2.2.md +26 -0
- package/docs/release-notes-0.2.3.md +59 -0
- package/docs/runtime-truth.md +31 -0
- package/docs/security-edge-deepening-proof.md +65 -0
- package/docs/voice-collaboration-proof.md +38 -0
- package/docs/wan-security-donor-proof.md +58 -0
- package/docs/wireless-advanced-proof.md +60 -0
- package/examples/README.md +11 -8
- package/examples/gallery.md +9 -3
- package/examples/index.json +3 -3
- package/package.json +20 -1
- package/references/curated-donor-registry.json +5 -2
- package/references/packettracer-feature-atlas.json +183 -0
- package/references/scenario-fixture-corpus.json +1 -0
- package/scripts/build_examples_index.py +26 -15
- package/scripts/coverage_matrix.py +1054 -25
- package/scripts/feature_atlas.py +293 -0
- package/scripts/generate_pkt.py +491 -34
- package/scripts/intent_parser.py +798 -8
- package/scripts/pkt_editor.py +651 -0
- package/scripts/remote_search.py +197 -21
- package/scripts/runtime_doctor.py +83 -10
- package/scripts/sample_catalog.py +266 -6
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,95 @@ CAPABILITY_PATTERNS = {
|
|
|
38
38
|
"ospf": [r"\bospf\b"],
|
|
39
39
|
"eigrp": [r"\beigrp\b"],
|
|
40
40
|
"rip": [r"\brip\b"],
|
|
41
|
+
"ospfv2": [r"\bospfv2\b", r"\bospf v2\b", r"\bsingle-area ospf\b", r"\brouter ospf\b"],
|
|
42
|
+
"eigrp_ipv4": [r"\beigrp ipv4\b", r"\bipv4 eigrp\b", r"\brouter eigrp\b"],
|
|
43
|
+
"ripv2": [r"\bripv2\b", r"\brip v2\b", r"\brip version 2\b", r"\brouter rip\b"],
|
|
44
|
+
"static_route": [r"\bstatic route\b", r"\bstatic-route\b", r"\bip route\b"],
|
|
45
|
+
"default_route": [r"\bdefault route\b", r"\bdefault-route\b", r"\b0\.0\.0\.0/0\b"],
|
|
46
|
+
"dhcp_relay": [r"\bdhcp relay\b", r"\bdhcp-relay\b", r"\bhelper-address\b", r"\bip helper\b"],
|
|
47
|
+
"nat_static": [r"\bstatic nat\b", r"\bnat static\b"],
|
|
48
|
+
"nat_dynamic": [r"\bdynamic nat\b", r"\bnat dynamic\b"],
|
|
49
|
+
"pat": [r"\bpat\b", r"\boverload\b"],
|
|
50
|
+
"ssh_ios": [r"\bssh\b", r"\bip ssh\b", r"\bcrypto key\b"],
|
|
51
|
+
"ntp_ios": [r"\bntp\b", r"\bntp server\b"],
|
|
52
|
+
"syslog_ios": [r"\bsyslog\b", r"\blogging host\b"],
|
|
53
|
+
"ipv6_slaac": [r"\bslaac\b", r"\bipv6\s+slaac\b"],
|
|
54
|
+
"dhcpv6_stateful": [r"\bdhcpv6\b", r"\bstateful dhcpv6\b", r"\bipv6 stateful\b"],
|
|
55
|
+
"dhcpv6_stateless": [r"\bstateless dhcpv6\b", r"\bipv6 stateless\b"],
|
|
56
|
+
"ipv6_prefix_delegation": [r"\bprefix delegation\b", r"\bipv6 prefix\b"],
|
|
57
|
+
"ipv6_dns_aaaa": [r"\baaaa\b", r"\bipv6 dns\b"],
|
|
58
|
+
"ipv6_tunneling": [r"\bipv6ip\b", r"\bipv6 tunnel\b", r"\bipv6 tunneling\b"],
|
|
59
|
+
"isatap": [r"\bisatap\b"],
|
|
60
|
+
"ospfv3": [r"\bospfv3\b", r"\bipv6 ospf\b"],
|
|
61
|
+
"eigrp_ipv6": [r"\bipv6 eigrp\b", r"\beigrp ipv6\b"],
|
|
62
|
+
"ripng": [r"\bripng\b", r"\bipv6 rip\b"],
|
|
63
|
+
"hsrp": [r"\bhsrp\b"],
|
|
41
64
|
"nat": [r"\bnat\b"],
|
|
42
65
|
"acl": [r"\bacl\b", r"\baccess-list\b"],
|
|
66
|
+
"dhcp_snooping": [r"\bdhcp snooping\b"],
|
|
67
|
+
"dai": [r"\bdai\b", r"\bdynamic arp inspection\b"],
|
|
68
|
+
"dot1x": [r"\bdot1x\b", r"\b802\.1x\b", r"\bnac\b"],
|
|
69
|
+
"lldp": [r"\blldp\b"],
|
|
70
|
+
"rep": [r"\brep\b"],
|
|
71
|
+
"snmp": [r"\bsnmp\b"],
|
|
72
|
+
"netflow": [r"\bnetflow\b"],
|
|
73
|
+
"span": [r"\bmonitor\s+session\b", r"\bspan\b", r"\brspan\b"],
|
|
74
|
+
"qos": [r"\bqos policy\b", r"\bqos class\b", r"\bclass-map\b", r"\bpolicy-map\b", r"\bquality of service\b"],
|
|
75
|
+
"port_security": [r"\bport security\b", r"\bport-security\b"],
|
|
76
|
+
"bgp": [r"\bbgp\b", r"\brouter bgp\b", r"\bremote-as\b"],
|
|
77
|
+
"stp": [r"\bstp\b", r"\bspanning tree\b", r"\bspanning-tree\b", r"\bpvst\b"],
|
|
78
|
+
"rstp": [r"\brstp\b", r"\brapid-pvst\b", r"\brapid pvst\b", r"\brapid spanning\b"],
|
|
79
|
+
"etherchannel": [r"\betherchannel\b", r"\bport-channel\b", r"\bchannel-group\b"],
|
|
80
|
+
"lacp": [r"\blacp\b"],
|
|
81
|
+
"pagp": [r"\bpagp\b"],
|
|
82
|
+
"vtp": [r"\bvtp\b"],
|
|
83
|
+
"dtp": [r"\bdtp\b", r"\bdynamic desirable\b", r"\bdynamic auto\b"],
|
|
43
84
|
"vpn": [r"\bvpn\b", r"\bsite-to-site\b", r"\bipsec\b", r"\btunnel\b"],
|
|
44
85
|
"ipsec": [r"\bipsec\b", r"\bike\b", r"\bphase 1\b", r"\bphase 2\b"],
|
|
45
86
|
"gre": [r"\bgre\b", r"\bgre tunnel\b"],
|
|
46
87
|
"ppp": [r"\bppp\b", r"\bchap\b", r"\bpap\b"],
|
|
88
|
+
"security_edge": [r"\bsecurity edge\b", r"\bsecurity appliance\b", r"\bfirewall\b", r"\basa\b"],
|
|
89
|
+
"asa_acl_nat": [r"\basa acl\b", r"\basa nat\b", r"\basa acl nat\b"],
|
|
90
|
+
"asa_service_policy": [r"\basa service policy\b", r"\bservice policy\b"],
|
|
91
|
+
"clientless_vpn": [r"\bclientless vpn\b"],
|
|
92
|
+
"cbac": [r"\bcbac\b"],
|
|
93
|
+
"zfw": [r"\bzfw\b", r"\bzone based firewall\b", r"\bzone-based firewall\b"],
|
|
94
|
+
"sniffer": [r"\bsniffer\b"],
|
|
47
95
|
"multilayer_switching": [r"\bmultilayer switch\b", r"\blayer 3 switch\b", r"\b3560\b", r"\bsvi\b"],
|
|
96
|
+
"wlc": [r"\bwlc\b", r"\bwireless lan controller\b"],
|
|
97
|
+
"wpa_enterprise": [r"\bwpa enterprise\b", r"\bwpa2 enterprise\b", r"\bwpa-enterprise\b", r"\bwpa2-enterprise\b", r"\b802\.1x wireless\b"],
|
|
98
|
+
"wep": [r"\bwep\b"],
|
|
99
|
+
"guest_wifi": [r"\bguest wifi\b", r"\bguest wlan\b"],
|
|
100
|
+
"beamforming": [r"\bbeamforming\b"],
|
|
101
|
+
"meraki": [r"\bmeraki\b"],
|
|
102
|
+
"cellular_5g": [r"\b5g\b", r"\bcellular\b", r"\bcell tower\b"],
|
|
103
|
+
"bluetooth": [r"\bbluetooth\b"],
|
|
104
|
+
"network_controller": [r"\bnetwork controller\b", r"\bnetcon\b"],
|
|
105
|
+
"python_programming": [r"\bpython programming\b", r"\bpython app\b"],
|
|
106
|
+
"javascript_programming": [r"\bjavascript\b", r"\bjs app\b"],
|
|
107
|
+
"blockly_programming": [r"\bblockly\b"],
|
|
108
|
+
"tcp_udp_app": [r"\btcp test app\b", r"\budp test app\b", r"\btcp app\b", r"\budp app\b"],
|
|
109
|
+
"vm_iox": [r"\biox\b", r"\bvm\b", r"\bvirtual machine\b"],
|
|
110
|
+
"voip": [r"\bvoip\b"],
|
|
111
|
+
"ip_phone": [r"\bip phone\b", r"\bphone\b"],
|
|
112
|
+
"call_manager": [r"\bcall manager\b", r"\btelephony\b"],
|
|
113
|
+
"linksys_voice": [r"\blinksys voice\b"],
|
|
114
|
+
"mqtt": [r"\bmqtt\b"],
|
|
115
|
+
"real_http": [r"\breal http\b"],
|
|
116
|
+
"real_websocket": [r"\bwebsocket\b", r"\breal websocket\b"],
|
|
117
|
+
"visual_scripting": [r"\bvisual scripting\b"],
|
|
118
|
+
"ptp": [r"\bptp\b", r"\bprecision time protocol\b"],
|
|
119
|
+
"profinet": [r"\bprofinet\b"],
|
|
120
|
+
"l2nat": [r"\bl2nat\b", r"\bl2 nat\b"],
|
|
121
|
+
"cyberobserver": [r"\bcyberobserver\b"],
|
|
122
|
+
"industrial_firewall": [r"\bindustrial firewall\b", r"\bisa3000\b"],
|
|
123
|
+
"coaxial": [r"\bcoaxial\b"],
|
|
124
|
+
"cable_dsl": [r"\bcable modem\b", r"\bdsl\b", r"\bdsl modem\b"],
|
|
125
|
+
"central_office": [r"\bcentral office\b"],
|
|
126
|
+
"cell_tower": [r"\bcell tower\b"],
|
|
127
|
+
"power_distribution": [r"\bpower distribution\b"],
|
|
128
|
+
"hot_swappable": [r"\bhot swappable\b", r"\bhot-swappable\b"],
|
|
129
|
+
"ios_license": [r"\bios license\b", r"\blicense\b", r"\bios15\b", r"\bios 15\b"],
|
|
48
130
|
}
|
|
49
131
|
|
|
50
132
|
DEVICE_SYNONYMS = {
|
|
@@ -97,6 +179,15 @@ NATURAL_DEVICE_ALIASES = {
|
|
|
97
179
|
NETWORK_STYLE_PATTERNS = {
|
|
98
180
|
"campus": [r"\bcampus\b", r"\bkampus\b", r"\bsobeli\b", r"\bdepart", r"\bdepartment\b"],
|
|
99
181
|
"branch": [r"\bbranch\b", r"\bfilial\b"],
|
|
182
|
+
"ipv6_routing": [r"\bipv6\b", r"\bslaac\b", r"\bdhcpv6\b", r"\bospfv3\b", r"\bhsrp\b"],
|
|
183
|
+
"ipv4_routing_management": [r"\bospfv2\b", r"\bospf v2\b", r"\beigrp ipv4\b", r"\bripv2\b", r"\brip version 2\b", r"\bstatic route\b", r"\bdefault route\b", r"\bdhcp relay\b", r"\bnat\b", r"\bpat\b", r"\bip ssh\b", r"\bntp server\b", r"\blogging host\b"],
|
|
184
|
+
"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"],
|
|
185
|
+
"l2_resiliency_routing": [r"\bbgp\b", r"\bstp\b", r"\brstp\b", r"\bspanning tree\b", r"\bspanning-tree\b", r"\betherchannel\b", r"\bport-channel\b", r"\bchannel-group\b", r"\blacp\b", r"\bpagp\b", r"\bvtp\b", r"\bdtp\b"],
|
|
186
|
+
"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"],
|
|
187
|
+
"automation_controller": [r"\bnetwork controller\b", r"\bnetcon\b", r"\bblockly\b", r"\bjavascript\b", r"\bpython programming\b", r"\biox\b"],
|
|
188
|
+
"voice_collaboration": [r"\bvoip\b", r"\bip phone\b", r"\bcall manager\b", r"\btelephony\b"],
|
|
189
|
+
"industrial_iot": [r"\bmqtt\b", r"\bwebsocket\b", r"\bprofinet\b", r"\bl2nat\b", r"\bptp\b", r"\bcyberobserver\b", r"\bindustrial\b"],
|
|
190
|
+
"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
191
|
"small_office": [r"\bsmall office\b", r"\bhome\b", r"\bofis\b"],
|
|
101
192
|
"wireless_branch": [r"\bwireless\b", r"\bwifi\b", r"\bssid\b"],
|
|
102
193
|
"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 +227,8 @@ SECURITY_TO_AUTH = {
|
|
|
136
227
|
"wpa": ("2", "2"),
|
|
137
228
|
"wpa2": ("4", "4"),
|
|
138
229
|
"802.1x": ("5", "5"),
|
|
230
|
+
"wpa-enterprise": ("5", "5"),
|
|
231
|
+
"wpa2-enterprise": ("5", "5"),
|
|
139
232
|
}
|
|
140
233
|
|
|
141
234
|
TRANSLITERATION_TABLE = str.maketrans(
|
|
@@ -163,7 +256,11 @@ TRANSLITERATION_TABLE = str.maketrans(
|
|
|
163
256
|
|
|
164
257
|
|
|
165
258
|
def _command_segments(prompt: str) -> list[str]:
|
|
166
|
-
parts = re.split(
|
|
259
|
+
parts = re.split(
|
|
260
|
+
r"(?=(?<!-)\b(?:device|connect|link|set|enable|associate|change|update|rename|apply)\b)",
|
|
261
|
+
prompt,
|
|
262
|
+
flags=re.IGNORECASE,
|
|
263
|
+
)
|
|
167
264
|
return [part.strip() for part in parts if part.strip()]
|
|
168
265
|
|
|
169
266
|
|
|
@@ -235,6 +332,7 @@ class IntentPlan:
|
|
|
235
332
|
management_ops: list[dict[str, object]] = field(default_factory=list)
|
|
236
333
|
verification_ops: list[dict[str, object]] = field(default_factory=list)
|
|
237
334
|
iot_ops: list[dict[str, object]] = field(default_factory=list)
|
|
335
|
+
programming_ops: list[dict[str, object]] = field(default_factory=list)
|
|
238
336
|
|
|
239
337
|
def all_operations(self) -> list[dict[str, object]]:
|
|
240
338
|
merged: list[dict[str, object]] = []
|
|
@@ -248,6 +346,7 @@ class IntentPlan:
|
|
|
248
346
|
self.management_ops,
|
|
249
347
|
self.verification_ops,
|
|
250
348
|
self.iot_ops,
|
|
349
|
+
self.programming_ops,
|
|
251
350
|
]:
|
|
252
351
|
merged.extend(bucket)
|
|
253
352
|
return merged
|
|
@@ -389,7 +488,7 @@ def _extract_service_requirements(capabilities: list[str], prompt: str) -> dict[
|
|
|
389
488
|
for service in ["dhcp", "dns", "http", "https", "ftp", "tftp", "ntp", "email", "syslog", "aaa"]:
|
|
390
489
|
if service in lowered:
|
|
391
490
|
requirements["services"].append(service)
|
|
392
|
-
for security in ["acl", "telnet", "wpa2", "wpa", "wep", "nat", "vpn", "ipsec", "gre", "ppp", "ssh"]:
|
|
491
|
+
for security in ["acl", "telnet", "wpa2", "wpa", "wep", "nat", "vpn", "ipsec", "gre", "ppp", "security_edge", "ssh"]:
|
|
393
492
|
if security in lowered:
|
|
394
493
|
requirements["security"].append(security)
|
|
395
494
|
return requirements
|
|
@@ -503,6 +602,122 @@ def _extract_switch_ops(prompt: str) -> list[dict[str, object]]:
|
|
|
503
602
|
"native": int(native) if native else None,
|
|
504
603
|
}
|
|
505
604
|
)
|
|
605
|
+
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)
|
|
606
|
+
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)
|
|
607
|
+
port_security_pattern = re.compile(
|
|
608
|
+
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))?",
|
|
609
|
+
flags=re.IGNORECASE,
|
|
610
|
+
)
|
|
611
|
+
lldp_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+lldp(?:\s+(?:enable|run))?", flags=re.IGNORECASE)
|
|
612
|
+
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)
|
|
613
|
+
span_pattern = re.compile(
|
|
614
|
+
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/._-]+)",
|
|
615
|
+
flags=re.IGNORECASE,
|
|
616
|
+
)
|
|
617
|
+
dot1x_pattern = re.compile(
|
|
618
|
+
r"set\s+([A-Za-z0-9_-]+)\s+dot1x\s+interface\s+([A-Za-z0-9/._-]+)\s+mode\s+(auto|force-authorized|force-unauthorized)"
|
|
619
|
+
r"(?:\s+radius\s+(\d+\.\d+\.\d+\.\d+)\s+key\s+([A-Za-z0-9._-]+))?",
|
|
620
|
+
flags=re.IGNORECASE,
|
|
621
|
+
)
|
|
622
|
+
qos_pattern = re.compile(
|
|
623
|
+
r"set\s+([A-Za-z0-9_-]+)\s+qos\s+class-map\s+([A-Za-z0-9_-]+)\s+match\s+(.+?)\s+policy-map\s+([A-Za-z0-9_-]+)\s+class\s+\2\s+(priority|bandwidth\s+\d+|police\s+\d+)"
|
|
624
|
+
r"\s+service-policy\s+(input|output)\s+([A-Za-z0-9/._-]+)(?=\s+set\s+|$)",
|
|
625
|
+
flags=re.IGNORECASE,
|
|
626
|
+
)
|
|
627
|
+
stp_pattern = re.compile(
|
|
628
|
+
r"set\s+([A-Za-z0-9_-]+)\s+stp\s+mode\s+([A-Za-z0-9_-]+)(?:\s+vlan\s+(\d+)\s+root\s+(primary|secondary))?",
|
|
629
|
+
flags=re.IGNORECASE,
|
|
630
|
+
)
|
|
631
|
+
etherchannel_pattern = re.compile(
|
|
632
|
+
r"set\s+([A-Za-z0-9_-]+)\s+etherchannel\s+(\d+)\s+mode\s+(active|passive|desirable|auto|on)\s+interfaces\s+(.+?)(?=\s+set\s+|$)",
|
|
633
|
+
flags=re.IGNORECASE,
|
|
634
|
+
)
|
|
635
|
+
vtp_pattern = re.compile(
|
|
636
|
+
r"set\s+([A-Za-z0-9_-]+)\s+vtp\s+domain\s+([A-Za-z0-9_.-]+)\s+mode\s+(server|client|transparent)(?:\s+version\s+(\d+))?",
|
|
637
|
+
flags=re.IGNORECASE,
|
|
638
|
+
)
|
|
639
|
+
dtp_pattern = re.compile(
|
|
640
|
+
r"set\s+([A-Za-z0-9_-]+)\s+dtp\s+interface\s+([A-Za-z0-9/._-]+)\s+mode\s+(dynamic\s+desirable|dynamic\s+auto|trunk|access)",
|
|
641
|
+
flags=re.IGNORECASE,
|
|
642
|
+
)
|
|
643
|
+
for segment in _command_segments(prompt):
|
|
644
|
+
for device, vlan_id, trust_port in dhcp_snooping_pattern.findall(segment):
|
|
645
|
+
ops.append({"op": "set_dhcp_snooping", "device": device, "vlan": int(vlan_id), "trust_port": trust_port or None})
|
|
646
|
+
for device, vlan_id, trust_port in dai_pattern.findall(segment):
|
|
647
|
+
ops.append({"op": "set_dai", "device": device, "vlan": int(vlan_id), "trust_port": trust_port or None})
|
|
648
|
+
for device, port, maximum, violation in port_security_pattern.findall(segment):
|
|
649
|
+
ops.append(
|
|
650
|
+
{
|
|
651
|
+
"op": "set_port_security",
|
|
652
|
+
"device": device,
|
|
653
|
+
"port": port,
|
|
654
|
+
"maximum": int(maximum) if maximum else None,
|
|
655
|
+
"violation": violation.lower() if violation else None,
|
|
656
|
+
}
|
|
657
|
+
)
|
|
658
|
+
for device in lldp_pattern.findall(segment):
|
|
659
|
+
ops.append({"op": "set_lldp", "device": device})
|
|
660
|
+
for device, segment_id, interface_name in rep_pattern.findall(segment):
|
|
661
|
+
ops.append({"op": "set_rep", "device": device, "segment": int(segment_id), "interface": interface_name})
|
|
662
|
+
for device, session, source, destination in span_pattern.findall(segment):
|
|
663
|
+
ops.append({"op": "set_span", "device": device, "session": int(session), "source": source, "destination": destination})
|
|
664
|
+
for device, interface_name, mode, radius_host, radius_key in dot1x_pattern.findall(segment):
|
|
665
|
+
ops.append(
|
|
666
|
+
{
|
|
667
|
+
"op": "set_dot1x",
|
|
668
|
+
"device": device,
|
|
669
|
+
"interface": interface_name,
|
|
670
|
+
"mode": mode.lower(),
|
|
671
|
+
"radius_host": radius_host or None,
|
|
672
|
+
"radius_key": radius_key or None,
|
|
673
|
+
}
|
|
674
|
+
)
|
|
675
|
+
for device, class_map, match_expr, policy_map, action, direction, interface_name in qos_pattern.findall(segment):
|
|
676
|
+
ops.append(
|
|
677
|
+
{
|
|
678
|
+
"op": "set_qos_policy",
|
|
679
|
+
"device": device,
|
|
680
|
+
"class_map": class_map,
|
|
681
|
+
"match": match_expr.strip(),
|
|
682
|
+
"policy_map": policy_map,
|
|
683
|
+
"action": action.lower(),
|
|
684
|
+
"direction": direction.lower(),
|
|
685
|
+
"interface": interface_name,
|
|
686
|
+
}
|
|
687
|
+
)
|
|
688
|
+
for device, mode, vlan_id, root_priority in stp_pattern.findall(segment):
|
|
689
|
+
ops.append(
|
|
690
|
+
{
|
|
691
|
+
"op": "set_stp",
|
|
692
|
+
"device": device,
|
|
693
|
+
"mode": mode.lower(),
|
|
694
|
+
"vlan": int(vlan_id) if vlan_id else None,
|
|
695
|
+
"root": root_priority.lower() if root_priority else None,
|
|
696
|
+
}
|
|
697
|
+
)
|
|
698
|
+
for device, channel, mode, interfaces_text in etherchannel_pattern.findall(segment):
|
|
699
|
+
interfaces = [value for value in re.findall(r"[A-Za-z]+[A-Za-z]*[0-9/._-]+", interfaces_text) if value.lower() != "set"]
|
|
700
|
+
ops.append(
|
|
701
|
+
{
|
|
702
|
+
"op": "set_etherchannel",
|
|
703
|
+
"device": device,
|
|
704
|
+
"channel": int(channel),
|
|
705
|
+
"mode": mode.lower(),
|
|
706
|
+
"interfaces": interfaces,
|
|
707
|
+
}
|
|
708
|
+
)
|
|
709
|
+
for device, domain, mode, version in vtp_pattern.findall(segment):
|
|
710
|
+
ops.append(
|
|
711
|
+
{
|
|
712
|
+
"op": "set_vtp",
|
|
713
|
+
"device": device,
|
|
714
|
+
"domain": domain,
|
|
715
|
+
"mode": mode.lower(),
|
|
716
|
+
"version": int(version) if version else None,
|
|
717
|
+
}
|
|
718
|
+
)
|
|
719
|
+
for device, interface_name, mode in dtp_pattern.findall(segment):
|
|
720
|
+
ops.append({"op": "set_dtp", "device": device, "interface": interface_name, "mode": mode.lower()})
|
|
506
721
|
return ops
|
|
507
722
|
|
|
508
723
|
|
|
@@ -570,6 +785,319 @@ def _extract_router_ops(prompt: str) -> list[dict[str, object]]:
|
|
|
570
785
|
for segment in _command_segments(prompt):
|
|
571
786
|
for acl_name, direction, device, interface_name in acl_apply_pattern.findall(segment):
|
|
572
787
|
ops.append({"op": "apply_acl", "device": device.strip(), "acl_name": acl_name, "direction": direction.lower(), "interface": interface_name})
|
|
788
|
+
ipv6_unicast_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+ipv6\s+unicast-routing", flags=re.IGNORECASE)
|
|
789
|
+
ipv6_address_pattern = re.compile(
|
|
790
|
+
r"set\s+([A-Za-z0-9_-]+)\s+(?:interface\s+)?([A-Za-z0-9/._-]+)\s+ipv6\s+([0-9A-Fa-f:]+)/(\d+)",
|
|
791
|
+
flags=re.IGNORECASE,
|
|
792
|
+
)
|
|
793
|
+
ipv6_slaac_pattern = re.compile(
|
|
794
|
+
r"set\s+([A-Za-z0-9_-]+)\s+slaac\s+on\s+([A-Za-z0-9/._-]+)(?:\s+prefix\s+([0-9A-Fa-f:]+)/(\d+))?",
|
|
795
|
+
flags=re.IGNORECASE,
|
|
796
|
+
)
|
|
797
|
+
dhcpv6_pattern = re.compile(
|
|
798
|
+
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/._-]+)"
|
|
799
|
+
r"(?:\s+dns\s+([0-9A-Fa-f:]+))?(?:\s+domain\s+([A-Za-z0-9._-]+))?",
|
|
800
|
+
flags=re.IGNORECASE,
|
|
801
|
+
)
|
|
802
|
+
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)
|
|
803
|
+
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)
|
|
804
|
+
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)
|
|
805
|
+
hsrp_pattern = re.compile(
|
|
806
|
+
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+))?",
|
|
807
|
+
flags=re.IGNORECASE,
|
|
808
|
+
)
|
|
809
|
+
ospfv2_pattern = re.compile(
|
|
810
|
+
r"set\s+([A-Za-z0-9_-]+)\s+ospfv2\s+(\d+)\s+network\s+(\d+\.\d+\.\d+\.\d+)\s+wildcard\s+(\d+\.\d+\.\d+\.\d+)\s+area\s+(\d+)",
|
|
811
|
+
flags=re.IGNORECASE,
|
|
812
|
+
)
|
|
813
|
+
eigrp_ipv4_pattern = re.compile(
|
|
814
|
+
r"set\s+([A-Za-z0-9_-]+)\s+eigrp\s+ipv4\s+(\d+)\s+network\s+(\d+\.\d+\.\d+\.\d+)\s+wildcard\s+(\d+\.\d+\.\d+\.\d+)(?:\s+(no-auto-summary))?",
|
|
815
|
+
flags=re.IGNORECASE,
|
|
816
|
+
)
|
|
817
|
+
ripv2_pattern = re.compile(
|
|
818
|
+
r"set\s+([A-Za-z0-9_-]+)\s+rip\s+version\s+2\s+network\s+(\d+\.\d+\.\d+\.\d+)(?:\s+(no-auto-summary))?",
|
|
819
|
+
flags=re.IGNORECASE,
|
|
820
|
+
)
|
|
821
|
+
static_route_pattern = re.compile(
|
|
822
|
+
r"set\s+([A-Za-z0-9_-]+)\s+static-route\s+(\d+\.\d+\.\d+\.\d+)/(\d+)\s+via\s+(\d+\.\d+\.\d+\.\d+)",
|
|
823
|
+
flags=re.IGNORECASE,
|
|
824
|
+
)
|
|
825
|
+
dhcp_relay_pattern = re.compile(
|
|
826
|
+
r"set\s+([A-Za-z0-9_-]+)\s+dhcp-relay\s+interface\s+([A-Za-z0-9/._-]+)\s+helper\s+(\d+\.\d+\.\d+\.\d+)",
|
|
827
|
+
flags=re.IGNORECASE,
|
|
828
|
+
)
|
|
829
|
+
nat_inside_outside_pattern = re.compile(
|
|
830
|
+
r"set\s+([A-Za-z0-9_-]+)\s+nat\s+(inside|outside)\s+interface\s+([A-Za-z0-9/._-]+)",
|
|
831
|
+
flags=re.IGNORECASE,
|
|
832
|
+
)
|
|
833
|
+
nat_static_pattern = re.compile(
|
|
834
|
+
r"set\s+([A-Za-z0-9_-]+)\s+nat\s+static\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+\.\d+\.\d+\.\d+)",
|
|
835
|
+
flags=re.IGNORECASE,
|
|
836
|
+
)
|
|
837
|
+
pat_pattern = re.compile(
|
|
838
|
+
r"set\s+([A-Za-z0-9_-]+)\s+pat\s+acl\s+([A-Za-z0-9_-]+)\s+interface\s+([A-Za-z0-9/._-]+)(?:\s+(overload))?",
|
|
839
|
+
flags=re.IGNORECASE,
|
|
840
|
+
)
|
|
841
|
+
ssh_ios_pattern = re.compile(
|
|
842
|
+
r"set\s+([A-Za-z0-9_-]+)\s+ssh\s+domain\s+([A-Za-z0-9._-]+)\s+username\s+([A-Za-z0-9._-]+)\s+password\s+([A-Za-z0-9._-]+)(?:\s+modulus\s+(\d+))?",
|
|
843
|
+
flags=re.IGNORECASE,
|
|
844
|
+
)
|
|
845
|
+
ntp_ios_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+ntp\s+server\s+(\d+\.\d+\.\d+\.\d+)", flags=re.IGNORECASE)
|
|
846
|
+
syslog_ios_pattern = re.compile(r"set\s+([A-Za-z0-9_-]+)\s+syslog\s+server\s+(\d+\.\d+\.\d+\.\d+)", flags=re.IGNORECASE)
|
|
847
|
+
bgp_pattern = re.compile(
|
|
848
|
+
r"set\s+([A-Za-z0-9_-]+)\s+bgp\s+(\d+)\s+neighbor\s+(\d+\.\d+\.\d+\.\d+)\s+remote-as\s+(\d+)"
|
|
849
|
+
r"(?:\s+network\s+(\d+\.\d+\.\d+\.\d+)\s+mask\s+(\d+\.\d+\.\d+\.\d+))?",
|
|
850
|
+
flags=re.IGNORECASE,
|
|
851
|
+
)
|
|
852
|
+
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)
|
|
853
|
+
netflow_pattern = re.compile(
|
|
854
|
+
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))?",
|
|
855
|
+
flags=re.IGNORECASE,
|
|
856
|
+
)
|
|
857
|
+
gre_tunnel_pattern = re.compile(
|
|
858
|
+
r"set\s+([A-Za-z0-9_-]+)\s+gre\s+tunnel\s+([A-Za-z0-9/._-]+)\s+source\s+([A-Za-z0-9/._:-]+)\s+destination\s+([A-Za-z0-9/._:-]+)"
|
|
859
|
+
r"(?:\s+ip\s+(\d+\.\d+\.\d+\.\d+)/(\d+))?",
|
|
860
|
+
flags=re.IGNORECASE,
|
|
861
|
+
)
|
|
862
|
+
ppp_pattern = re.compile(
|
|
863
|
+
r"set\s+([A-Za-z0-9_-]+)\s+ppp\s+interface\s+([A-Za-z0-9/._-]+)(?:\s+authentication\s+(chap|pap))?",
|
|
864
|
+
flags=re.IGNORECASE,
|
|
865
|
+
)
|
|
866
|
+
ipsec_transform_pattern = re.compile(
|
|
867
|
+
r"set\s+([A-Za-z0-9_-]+)\s+ipsec\s+transform-set\s+([A-Za-z0-9_-]+)\s+([A-Za-z0-9_-]+)\s+([A-Za-z0-9_-]+)",
|
|
868
|
+
flags=re.IGNORECASE,
|
|
869
|
+
)
|
|
870
|
+
crypto_map_pattern = re.compile(
|
|
871
|
+
r"set\s+([A-Za-z0-9_-]+)\s+crypto\s+map\s+([A-Za-z0-9_-]+)\s+(\d+)\s+peer\s+(\d+\.\d+\.\d+\.\d+)\s+transform-set\s+([A-Za-z0-9_-]+)\s+match\s+([A-Za-z0-9_-]+)(?:\s+interface\s+([A-Za-z0-9/._-]+))?",
|
|
872
|
+
flags=re.IGNORECASE,
|
|
873
|
+
)
|
|
874
|
+
cbac_pattern = re.compile(
|
|
875
|
+
r"set\s+([A-Za-z0-9_-]+)\s+cbac\s+inspect\s+([A-Za-z0-9_-]+)\s+protocol\s+([A-Za-z0-9_-]+)\s+interface\s+([A-Za-z0-9/._-]+)\s+direction\s+(in|out)",
|
|
876
|
+
flags=re.IGNORECASE,
|
|
877
|
+
)
|
|
878
|
+
zfw_zone_interface_pattern = re.compile(
|
|
879
|
+
r"set\s+([A-Za-z0-9_-]+)\s+zfw\s+zone\s+([A-Za-z0-9_-]+)\s+interface\s+([A-Za-z0-9/._-]+)",
|
|
880
|
+
flags=re.IGNORECASE,
|
|
881
|
+
)
|
|
882
|
+
zfw_zone_pair_pattern = re.compile(
|
|
883
|
+
r"set\s+([A-Za-z0-9_-]+)\s+zfw\s+zone-pair\s+([A-Za-z0-9_-]+)\s+source\s+([A-Za-z0-9_-]+)\s+destination\s+([A-Za-z0-9_-]+)\s+policy\s+([A-Za-z0-9_-]+)",
|
|
884
|
+
flags=re.IGNORECASE,
|
|
885
|
+
)
|
|
886
|
+
zfw_policy_pattern = re.compile(
|
|
887
|
+
r"set\s+([A-Za-z0-9_-]+)\s+zfw\s+class-map\s+([A-Za-z0-9_-]+)\s+match\s+protocol\s+([A-Za-z0-9_-]+)\s+policy-map\s+([A-Za-z0-9_-]+)\s+action\s+(inspect|pass|drop)",
|
|
888
|
+
flags=re.IGNORECASE,
|
|
889
|
+
)
|
|
890
|
+
telephony_service_pattern = re.compile(
|
|
891
|
+
r'set\s+"?([^"]+?)"?\s+telephony\s+service\s+source-address\s+(\d+\.\d+\.\d+\.\d+)\s+port\s+(\d+)'
|
|
892
|
+
r"(?:\s+max-ephones\s+(\d+))?(?:\s+max-dn\s+(\d+))?",
|
|
893
|
+
flags=re.IGNORECASE,
|
|
894
|
+
)
|
|
895
|
+
ephone_dn_pattern = re.compile(r'set\s+"?([^"]+?)"?\s+ephone-dn\s+(\d+)\s+number\s+([A-Za-z0-9*+#.-]+)', flags=re.IGNORECASE)
|
|
896
|
+
ephone_pattern = re.compile(
|
|
897
|
+
r'set\s+"?([^"]+?)"?\s+ephone\s+(\d+)\s+mac\s+([0-9A-Fa-f.:-]+)\s+button\s+([0-9:]+)',
|
|
898
|
+
flags=re.IGNORECASE,
|
|
899
|
+
)
|
|
900
|
+
dial_peer_pattern = re.compile(
|
|
901
|
+
r'set\s+"?([^"]+?)"?\s+dial-peer\s+voice\s+(\d+)\s+destination-pattern\s+(\S+)\s+session-target\s+ipv4:(\d+\.\d+\.\d+\.\d+)',
|
|
902
|
+
flags=re.IGNORECASE,
|
|
903
|
+
)
|
|
904
|
+
for segment in _command_segments(prompt):
|
|
905
|
+
for device in ipv6_unicast_pattern.findall(segment):
|
|
906
|
+
ops.append({"op": "enable_ipv6_unicast_routing", "device": device})
|
|
907
|
+
for device, interface_name, address, prefix in ipv6_address_pattern.findall(segment):
|
|
908
|
+
ops.append({"op": "set_ipv6_address", "device": device, "interface": interface_name, "address": address, "prefix": int(prefix)})
|
|
909
|
+
for device, interface_name, prefix_address, prefix_len in ipv6_slaac_pattern.findall(segment):
|
|
910
|
+
ops.append(
|
|
911
|
+
{
|
|
912
|
+
"op": "set_ipv6_slaac",
|
|
913
|
+
"device": device,
|
|
914
|
+
"interface": interface_name,
|
|
915
|
+
"prefix": prefix_address or None,
|
|
916
|
+
"prefix_len": int(prefix_len) if prefix_len else None,
|
|
917
|
+
}
|
|
918
|
+
)
|
|
919
|
+
for device, name, prefix_address, prefix_len, interface_name, dns, domain in dhcpv6_pattern.findall(segment):
|
|
920
|
+
ops.append(
|
|
921
|
+
{
|
|
922
|
+
"op": "set_dhcpv6_pool",
|
|
923
|
+
"device": device,
|
|
924
|
+
"name": name,
|
|
925
|
+
"prefix": prefix_address,
|
|
926
|
+
"prefix_len": int(prefix_len),
|
|
927
|
+
"interface": interface_name,
|
|
928
|
+
"dns": dns or None,
|
|
929
|
+
"domain": domain or None,
|
|
930
|
+
}
|
|
931
|
+
)
|
|
932
|
+
for device, process_id, area, interface_name in ospfv3_pattern.findall(segment):
|
|
933
|
+
ops.append({"op": "set_ospfv3_interface", "device": device, "process_id": int(process_id), "area": int(area), "interface": interface_name})
|
|
934
|
+
for device, asn, interface_name in eigrp_ipv6_pattern.findall(segment):
|
|
935
|
+
ops.append({"op": "set_eigrp_ipv6_interface", "device": device, "asn": int(asn), "interface": interface_name})
|
|
936
|
+
for device, process_name, interface_name in ripng_pattern.findall(segment):
|
|
937
|
+
ops.append({"op": "set_ripng_interface", "device": device, "process_name": process_name, "interface": interface_name})
|
|
938
|
+
for device, group, virtual_ipv6, interface_name, priority in hsrp_pattern.findall(segment):
|
|
939
|
+
ops.append(
|
|
940
|
+
{
|
|
941
|
+
"op": "set_hsrp_ipv6",
|
|
942
|
+
"device": device,
|
|
943
|
+
"group": int(group),
|
|
944
|
+
"virtual_ipv6": virtual_ipv6,
|
|
945
|
+
"interface": interface_name,
|
|
946
|
+
"priority": int(priority) if priority else None,
|
|
947
|
+
}
|
|
948
|
+
)
|
|
949
|
+
for device, process_id, network, wildcard, area in ospfv2_pattern.findall(segment):
|
|
950
|
+
ops.append({"op": "set_ospfv2_network", "device": device, "process_id": int(process_id), "network": network, "wildcard": wildcard, "area": int(area)})
|
|
951
|
+
for device, asn, network, wildcard, no_auto in eigrp_ipv4_pattern.findall(segment):
|
|
952
|
+
ops.append({"op": "set_eigrp_ipv4_network", "device": device, "asn": int(asn), "network": network, "wildcard": wildcard, "no_auto_summary": bool(no_auto)})
|
|
953
|
+
for device, network, no_auto in ripv2_pattern.findall(segment):
|
|
954
|
+
ops.append({"op": "set_ripv2_network", "device": device, "network": network, "no_auto_summary": bool(no_auto)})
|
|
955
|
+
for device, network, prefix, next_hop in static_route_pattern.findall(segment):
|
|
956
|
+
ops.append({"op": "set_static_route", "device": device, "network": network, "prefix": int(prefix), "next_hop": next_hop})
|
|
957
|
+
for device, interface_name, helper in dhcp_relay_pattern.findall(segment):
|
|
958
|
+
ops.append({"op": "set_dhcp_relay", "device": device, "interface": interface_name, "helper": helper})
|
|
959
|
+
for device, role, interface_name in nat_inside_outside_pattern.findall(segment):
|
|
960
|
+
ops.append({"op": "set_nat_interface", "device": device, "role": role.lower(), "interface": interface_name})
|
|
961
|
+
for device, inside_local, inside_global in nat_static_pattern.findall(segment):
|
|
962
|
+
ops.append({"op": "set_nat_static", "device": device, "inside_local": inside_local, "inside_global": inside_global})
|
|
963
|
+
for device, acl, interface_name, overload in pat_pattern.findall(segment):
|
|
964
|
+
ops.append({"op": "set_pat_overload", "device": device, "acl": acl, "interface": interface_name, "overload": bool(overload)})
|
|
965
|
+
for device, domain, username, password, modulus in ssh_ios_pattern.findall(segment):
|
|
966
|
+
ops.append({"op": "set_ssh_ios", "device": device, "domain": domain, "username": username, "password": password, "modulus": int(modulus) if modulus else 1024})
|
|
967
|
+
for device, server in ntp_ios_pattern.findall(segment):
|
|
968
|
+
ops.append({"op": "set_ntp_server", "device": device, "server": server})
|
|
969
|
+
for device, server in syslog_ios_pattern.findall(segment):
|
|
970
|
+
ops.append({"op": "set_syslog_server", "device": device, "server": server})
|
|
971
|
+
for device, asn, neighbor, remote_as, network, mask in bgp_pattern.findall(segment):
|
|
972
|
+
ops.append(
|
|
973
|
+
{
|
|
974
|
+
"op": "set_bgp_neighbor",
|
|
975
|
+
"device": device,
|
|
976
|
+
"asn": int(asn),
|
|
977
|
+
"neighbor": neighbor,
|
|
978
|
+
"remote_as": int(remote_as),
|
|
979
|
+
"network": network or None,
|
|
980
|
+
"mask": mask or None,
|
|
981
|
+
}
|
|
982
|
+
)
|
|
983
|
+
for device, community, mode in snmp_pattern.findall(segment):
|
|
984
|
+
ops.append({"op": "set_snmp_community", "device": device, "community": community, "mode": mode.lower()})
|
|
985
|
+
for device, destination, port, version, interface_name, direction in netflow_pattern.findall(segment):
|
|
986
|
+
ops.append(
|
|
987
|
+
{
|
|
988
|
+
"op": "set_netflow",
|
|
989
|
+
"device": device,
|
|
990
|
+
"destination": destination,
|
|
991
|
+
"port": int(port),
|
|
992
|
+
"version": int(version) if version else 9,
|
|
993
|
+
"interface": interface_name or None,
|
|
994
|
+
"direction": direction.lower() if direction else None,
|
|
995
|
+
}
|
|
996
|
+
)
|
|
997
|
+
for device, tunnel_interface, source, destination, ip, prefix in gre_tunnel_pattern.findall(segment):
|
|
998
|
+
ops.append(
|
|
999
|
+
{
|
|
1000
|
+
"op": "set_gre_tunnel",
|
|
1001
|
+
"device": device,
|
|
1002
|
+
"interface": tunnel_interface,
|
|
1003
|
+
"source": source,
|
|
1004
|
+
"destination": destination,
|
|
1005
|
+
"ip": ip or None,
|
|
1006
|
+
"prefix": int(prefix) if prefix else None,
|
|
1007
|
+
}
|
|
1008
|
+
)
|
|
1009
|
+
for device, interface_name, authentication in ppp_pattern.findall(segment):
|
|
1010
|
+
ops.append(
|
|
1011
|
+
{
|
|
1012
|
+
"op": "set_ppp_interface",
|
|
1013
|
+
"device": device,
|
|
1014
|
+
"interface": interface_name,
|
|
1015
|
+
"authentication": authentication.lower() if authentication else None,
|
|
1016
|
+
}
|
|
1017
|
+
)
|
|
1018
|
+
for device, name, encryption, integrity in ipsec_transform_pattern.findall(segment):
|
|
1019
|
+
ops.append(
|
|
1020
|
+
{
|
|
1021
|
+
"op": "set_ipsec_transform_set",
|
|
1022
|
+
"device": device,
|
|
1023
|
+
"name": name,
|
|
1024
|
+
"encryption": encryption,
|
|
1025
|
+
"integrity": integrity,
|
|
1026
|
+
}
|
|
1027
|
+
)
|
|
1028
|
+
for device, map_name, sequence, peer, transform_set, acl_name, interface_name in crypto_map_pattern.findall(segment):
|
|
1029
|
+
ops.append(
|
|
1030
|
+
{
|
|
1031
|
+
"op": "set_crypto_map",
|
|
1032
|
+
"device": device,
|
|
1033
|
+
"map_name": map_name,
|
|
1034
|
+
"sequence": int(sequence),
|
|
1035
|
+
"peer": peer,
|
|
1036
|
+
"transform_set": transform_set,
|
|
1037
|
+
"acl_name": acl_name,
|
|
1038
|
+
"interface": interface_name or None,
|
|
1039
|
+
}
|
|
1040
|
+
)
|
|
1041
|
+
for device, name, protocol, interface_name, direction in cbac_pattern.findall(segment):
|
|
1042
|
+
ops.append(
|
|
1043
|
+
{
|
|
1044
|
+
"op": "set_cbac_inspect",
|
|
1045
|
+
"device": device,
|
|
1046
|
+
"name": name,
|
|
1047
|
+
"protocol": protocol.lower(),
|
|
1048
|
+
"interface": interface_name,
|
|
1049
|
+
"direction": direction.lower(),
|
|
1050
|
+
}
|
|
1051
|
+
)
|
|
1052
|
+
for device, zone, interface_name in zfw_zone_interface_pattern.findall(segment):
|
|
1053
|
+
ops.append({"op": "set_zfw_zone_interface", "device": device, "zone": zone, "interface": interface_name})
|
|
1054
|
+
for device, pair_name, source, destination, policy in zfw_zone_pair_pattern.findall(segment):
|
|
1055
|
+
ops.append(
|
|
1056
|
+
{
|
|
1057
|
+
"op": "set_zfw_zone_pair",
|
|
1058
|
+
"device": device,
|
|
1059
|
+
"pair_name": pair_name,
|
|
1060
|
+
"source": source,
|
|
1061
|
+
"destination": destination,
|
|
1062
|
+
"policy": policy,
|
|
1063
|
+
}
|
|
1064
|
+
)
|
|
1065
|
+
for device, class_map, protocol, policy_map, action in zfw_policy_pattern.findall(segment):
|
|
1066
|
+
ops.append(
|
|
1067
|
+
{
|
|
1068
|
+
"op": "set_zfw_policy",
|
|
1069
|
+
"device": device,
|
|
1070
|
+
"class_map": class_map,
|
|
1071
|
+
"protocol": protocol.lower(),
|
|
1072
|
+
"policy_map": policy_map,
|
|
1073
|
+
"action": action.lower(),
|
|
1074
|
+
}
|
|
1075
|
+
)
|
|
1076
|
+
for device, source_address, port, max_ephones, max_dn in telephony_service_pattern.findall(segment):
|
|
1077
|
+
ops.append(
|
|
1078
|
+
{
|
|
1079
|
+
"op": "set_telephony_service",
|
|
1080
|
+
"device": device.strip(),
|
|
1081
|
+
"source_address": source_address,
|
|
1082
|
+
"port": int(port),
|
|
1083
|
+
"max_ephones": int(max_ephones) if max_ephones else None,
|
|
1084
|
+
"max_dn": int(max_dn) if max_dn else None,
|
|
1085
|
+
}
|
|
1086
|
+
)
|
|
1087
|
+
for device, dn_id, number in ephone_dn_pattern.findall(segment):
|
|
1088
|
+
ops.append({"op": "set_ephone_dn", "device": device.strip(), "dn_id": int(dn_id), "number": number})
|
|
1089
|
+
for device, ephone_id, mac, button in ephone_pattern.findall(segment):
|
|
1090
|
+
ops.append({"op": "set_ephone", "device": device.strip(), "ephone_id": int(ephone_id), "mac": mac.upper(), "button": button})
|
|
1091
|
+
for device, peer_id, destination_pattern, session_target in dial_peer_pattern.findall(segment):
|
|
1092
|
+
ops.append(
|
|
1093
|
+
{
|
|
1094
|
+
"op": "set_dial_peer_voice",
|
|
1095
|
+
"device": device.strip(),
|
|
1096
|
+
"peer_id": int(peer_id),
|
|
1097
|
+
"destination_pattern": destination_pattern,
|
|
1098
|
+
"session_target": session_target,
|
|
1099
|
+
}
|
|
1100
|
+
)
|
|
573
1101
|
return ops
|
|
574
1102
|
|
|
575
1103
|
|
|
@@ -630,14 +1158,31 @@ def _extract_management_ops(prompt: str) -> list[dict[str, object]]:
|
|
|
630
1158
|
def _extract_wireless_ops(prompt: str) -> list[dict[str, object]]:
|
|
631
1159
|
ops: list[dict[str, object]] = []
|
|
632
1160
|
ssid_pattern = re.compile(
|
|
633
|
-
r"set\s+([A-Za-z0-9_ -]+?)\s+ssid\s+([A-Za-z0-9._-]+)
|
|
1161
|
+
r"set\s+([A-Za-z0-9_ -]+?)\s+ssid\s+([A-Za-z0-9._-]+)"
|
|
1162
|
+
r"(?:\s+security\s+(wpa2?\s+enterprise|wpa2?-enterprise|wpa2?-psk|802\.1x|wep|open|wpa2?|wpa))?"
|
|
1163
|
+
r"(?:\s+passphrase\s+([A-Za-z0-9._-]+))?"
|
|
1164
|
+
r"(?:\s+radius\s+(\d+\.\d+\.\d+\.\d+)\s+secret\s+([A-Za-z0-9._-]+))?"
|
|
1165
|
+
r"(?:\s+channel\s+(\d+))?",
|
|
634
1166
|
flags=re.IGNORECASE,
|
|
635
1167
|
)
|
|
636
1168
|
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()
|
|
1169
|
+
for device, ssid, security, passphrase, radius_server, radius_secret, channel in ssid_pattern.findall(segment):
|
|
1170
|
+
security_key = (security or "open").lower().replace(" ", "-")
|
|
639
1171
|
auth_type, encrypt_type = SECURITY_TO_AUTH.get(security_key, ("0", "0"))
|
|
640
|
-
|
|
1172
|
+
operation = {
|
|
1173
|
+
"op": "set_wireless_ssid",
|
|
1174
|
+
"device": device.strip(),
|
|
1175
|
+
"ssid": ssid,
|
|
1176
|
+
"security": security_key,
|
|
1177
|
+
"auth_type": auth_type,
|
|
1178
|
+
"encrypt_type": encrypt_type,
|
|
1179
|
+
"passphrase": passphrase or "",
|
|
1180
|
+
"channel": int(channel) if channel else 1,
|
|
1181
|
+
}
|
|
1182
|
+
if radius_server:
|
|
1183
|
+
operation["radius_server"] = radius_server
|
|
1184
|
+
operation["radius_secret"] = radius_secret or ""
|
|
1185
|
+
ops.append(operation)
|
|
641
1186
|
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
1187
|
for segment in _command_segments(prompt):
|
|
643
1188
|
for client, ap, ssid, ip_mode in assoc_pattern.findall(segment):
|
|
@@ -724,6 +1269,36 @@ def _extract_iot_ops(prompt: str) -> list[dict[str, object]]:
|
|
|
724
1269
|
return ops
|
|
725
1270
|
|
|
726
1271
|
|
|
1272
|
+
def _decode_prompt_string(value: str) -> str:
|
|
1273
|
+
return (
|
|
1274
|
+
value.replace(r"\\", "\0")
|
|
1275
|
+
.replace(r"\"", '"')
|
|
1276
|
+
.replace(r"\n", "\n")
|
|
1277
|
+
.replace(r"\r", "\r")
|
|
1278
|
+
.replace(r"\t", "\t")
|
|
1279
|
+
.replace("\0", "\\")
|
|
1280
|
+
)
|
|
1281
|
+
|
|
1282
|
+
|
|
1283
|
+
def _extract_programming_ops(prompt: str) -> list[dict[str, object]]:
|
|
1284
|
+
ops: list[dict[str, object]] = []
|
|
1285
|
+
script_pattern = re.compile(
|
|
1286
|
+
r'set\s+"((?:\\.|[^"\\])*)"\s+script\s+app\s+"((?:\\.|[^"\\])*)"\s+file\s+"((?:\\.|[^"\\])*)"\s+content\s+"((?:\\.|[^"\\])*)"',
|
|
1287
|
+
flags=re.IGNORECASE | re.DOTALL,
|
|
1288
|
+
)
|
|
1289
|
+
for device, app_name, file_name, content in script_pattern.findall(prompt):
|
|
1290
|
+
ops.append(
|
|
1291
|
+
{
|
|
1292
|
+
"op": "set_script_file_content",
|
|
1293
|
+
"device": _decode_prompt_string(device).strip(),
|
|
1294
|
+
"app_name": _decode_prompt_string(app_name).strip(),
|
|
1295
|
+
"file_name": _decode_prompt_string(file_name).strip(),
|
|
1296
|
+
"content": _decode_prompt_string(content),
|
|
1297
|
+
}
|
|
1298
|
+
)
|
|
1299
|
+
return ops
|
|
1300
|
+
|
|
1301
|
+
|
|
727
1302
|
def parse_intent(prompt: str) -> IntentPlan:
|
|
728
1303
|
pkt_path_match = re.search(r"([A-Za-z]:\\[^\"\n]+?\.pkt)\b", prompt, flags=re.IGNORECASE)
|
|
729
1304
|
pkt_path = pkt_path_match.group(1) if pkt_path_match else None
|
|
@@ -739,6 +1314,64 @@ def parse_intent(prompt: str) -> IntentPlan:
|
|
|
739
1314
|
host_link_intent = _extract_host_link_intent(normalized_prompt)
|
|
740
1315
|
host_vlan_assignment = _extract_host_vlan_assignment(normalized_prompt)
|
|
741
1316
|
network_style = _extract_network_style(normalized_prompt)
|
|
1317
|
+
if (
|
|
1318
|
+
network_style == "wireless_advanced"
|
|
1319
|
+
and "server_aaa" in capability_set
|
|
1320
|
+
and re.search(r"\bradius\b", lowered)
|
|
1321
|
+
and not re.search(r"\b(?:aaa|acs|server aaa|aaa server)\b", lowered)
|
|
1322
|
+
):
|
|
1323
|
+
capability_set.discard("server_aaa")
|
|
1324
|
+
if re.search(r"\bqos\b", lowered) and (
|
|
1325
|
+
re.search(r"\b(?:snmp|netflow|span|rspan|lldp|rep|dai|dot1x|802\.1x|dhcp snooping|port-security|port security)\b", lowered)
|
|
1326
|
+
or network_style == "l2_security_monitoring"
|
|
1327
|
+
):
|
|
1328
|
+
capability_set.add("qos")
|
|
1329
|
+
if network_style == "automation_controller" or re.search(r"\b(?:network controller|netcon|blockly|iox)\b", lowered):
|
|
1330
|
+
if re.search(r"\bpython\b", lowered):
|
|
1331
|
+
capability_set.add("python_programming")
|
|
1332
|
+
if re.search(r"\b(?:tcp|udp)\b", lowered) and re.search(r"\b(?:app|application|test app)\b", lowered):
|
|
1333
|
+
capability_set.add("tcp_udp_app")
|
|
1334
|
+
if "ospf" in capability_set and not re.search(r"\b(?:ipv6|ospfv3)\b", lowered):
|
|
1335
|
+
capability_set.add("ospfv2")
|
|
1336
|
+
if "eigrp" in capability_set and not re.search(r"\bipv6\b", lowered):
|
|
1337
|
+
capability_set.add("eigrp_ipv4")
|
|
1338
|
+
if "rip" in capability_set and not re.search(r"\b(?:ipv6|ripng)\b", lowered):
|
|
1339
|
+
capability_set.add("ripv2")
|
|
1340
|
+
if "nat" in capability_set:
|
|
1341
|
+
if re.search(r"\bpat\b|\boverload\b", lowered):
|
|
1342
|
+
capability_set.add("pat")
|
|
1343
|
+
if re.search(r"\bstatic\s+nat\b|\bnat\s+static\b", lowered):
|
|
1344
|
+
capability_set.add("nat_static")
|
|
1345
|
+
if re.search(r"\bdynamic\s+nat\b|\bnat\s+dynamic\b", lowered):
|
|
1346
|
+
capability_set.add("nat_dynamic")
|
|
1347
|
+
if network_style == "ipv4_routing_management" and not {"nat_static", "nat_dynamic"} & capability_set:
|
|
1348
|
+
capability_set.update({"nat_static", "nat_dynamic"})
|
|
1349
|
+
if "ssh" in capability_set:
|
|
1350
|
+
capability_set.add("ssh_ios")
|
|
1351
|
+
if "ntp" in capability_set:
|
|
1352
|
+
capability_set.add("ntp_ios")
|
|
1353
|
+
if (
|
|
1354
|
+
"server_syslog" in capability_set
|
|
1355
|
+
and re.search(r"\b(?:server|ftp|email|aaa|dns)\b", lowered)
|
|
1356
|
+
and not re.search(r"\b(?:logging\s+host|set\s+\S+\s+syslog\s+server)\b", lowered)
|
|
1357
|
+
):
|
|
1358
|
+
capability_set.discard("syslog_ios")
|
|
1359
|
+
if re.search(r"\blogging\s+host\b", lowered):
|
|
1360
|
+
capability_set.add("syslog_ios")
|
|
1361
|
+
if re.search(r"\bdefault\s+route\b|\b0\.0\.0\.0/0\b", lowered):
|
|
1362
|
+
capability_set.update({"static_route", "default_route"})
|
|
1363
|
+
elif re.search(r"\bstatic\s+route\b|\bip\s+route\b", lowered):
|
|
1364
|
+
capability_set.add("static_route")
|
|
1365
|
+
if re.search(r"\bdhcp\s+relay\b|\bdhcp-relay\b|\bhelper-address\b|\bip\s+helper\b", lowered):
|
|
1366
|
+
capability_set.add("dhcp_relay")
|
|
1367
|
+
if not re.search(r"\b(?:dhcp\s+pool|server\s+dhcp|dhcp\s+server|default-router)\b", lowered):
|
|
1368
|
+
capability_set.discard("dhcp_pool")
|
|
1369
|
+
capability_set.discard("router_dhcp")
|
|
1370
|
+
capability_set.discard("server_dhcp")
|
|
1371
|
+
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):
|
|
1372
|
+
capability_set.discard("dhcp_pool")
|
|
1373
|
+
capability_set.discard("router_dhcp")
|
|
1374
|
+
capability_set.discard("server_dhcp")
|
|
742
1375
|
department_count = _extract_department_count(normalized_prompt)
|
|
743
1376
|
per_department_devices = _extract_per_department_devices(normalized_prompt)
|
|
744
1377
|
|
|
@@ -788,17 +1421,173 @@ def parse_intent(prompt: str) -> IntentPlan:
|
|
|
788
1421
|
management_ops = _extract_management_ops(prompt)
|
|
789
1422
|
verification_ops = _extract_verification_ops(prompt)
|
|
790
1423
|
iot_ops = _extract_iot_ops(prompt)
|
|
1424
|
+
programming_ops = _extract_programming_ops(prompt)
|
|
791
1425
|
|
|
792
1426
|
if any(op["op"] == "associate_wireless_client" for op in wireless_ops):
|
|
793
1427
|
capability_set.update({"wireless_client", "wireless_client_association"})
|
|
794
1428
|
if any(op["op"] == "set_wireless_ssid" for op in wireless_ops):
|
|
795
1429
|
capability_set.update({"wireless_ap", "wireless_mutation"})
|
|
1430
|
+
for op in wireless_ops:
|
|
1431
|
+
if op.get("op") != "set_wireless_ssid":
|
|
1432
|
+
continue
|
|
1433
|
+
security = str(op.get("security") or "")
|
|
1434
|
+
if security == "wep":
|
|
1435
|
+
capability_set.add("wep")
|
|
1436
|
+
if security in {"wpa-enterprise", "wpa2-enterprise", "802.1x"} or op.get("radius_server"):
|
|
1437
|
+
capability_set.add("wpa_enterprise")
|
|
1438
|
+
elif network_style == "wireless_advanced":
|
|
1439
|
+
capability_set.discard("wireless_ap")
|
|
1440
|
+
capability_set.discard("wireless_mutation")
|
|
796
1441
|
if end_device_ops:
|
|
797
1442
|
capability_set.add("end_device_mutation")
|
|
798
1443
|
if iot_ops:
|
|
799
1444
|
capability_set.update({"iot", "iot_registration"})
|
|
800
|
-
|
|
1445
|
+
if any(op.get("op") == "set_iot_rule_state" for op in iot_ops):
|
|
1446
|
+
capability_set.add("iot_control")
|
|
1447
|
+
for operation in programming_ops:
|
|
1448
|
+
file_name = str(operation.get("file_name") or "").lower()
|
|
1449
|
+
app_name = str(operation.get("app_name") or "").lower()
|
|
1450
|
+
content = str(operation.get("content") or "").lower()
|
|
1451
|
+
if file_name.endswith(".py") or "python" in app_name:
|
|
1452
|
+
capability_set.add("python_programming")
|
|
1453
|
+
if file_name.endswith(".js") or "javascript" in app_name:
|
|
1454
|
+
capability_set.add("javascript_programming")
|
|
1455
|
+
if file_name.endswith(".visual") or "visual" in app_name or "<xml" in content:
|
|
1456
|
+
capability_set.add("blockly_programming")
|
|
1457
|
+
if "tcp" in " ".join([file_name, app_name, content]) or "udp" in " ".join([file_name, app_name, content]):
|
|
1458
|
+
capability_set.add("tcp_udp_app")
|
|
1459
|
+
if "realhttp" in content or "real http" in app_name:
|
|
1460
|
+
capability_set.add("real_http")
|
|
1461
|
+
if "realws" in content or "websocket" in content or "websocket" in app_name:
|
|
1462
|
+
capability_set.add("real_websocket")
|
|
1463
|
+
if "mqtt" in content or "mqtt" in app_name:
|
|
1464
|
+
capability_set.add("mqtt")
|
|
1465
|
+
if programming_ops and capability_set & {"python_programming", "javascript_programming", "blockly_programming"} and not capability_set & {"real_http", "real_websocket", "mqtt"}:
|
|
1466
|
+
network_style = network_style or "automation_controller"
|
|
1467
|
+
switch_op_names = {str(op.get("op")) for op in switch_ops}
|
|
1468
|
+
if "set_dhcp_snooping" in switch_op_names:
|
|
1469
|
+
capability_set.add("dhcp_snooping")
|
|
1470
|
+
if "set_dai" in switch_op_names:
|
|
1471
|
+
capability_set.add("dai")
|
|
1472
|
+
if "set_lldp" in switch_op_names:
|
|
1473
|
+
capability_set.add("lldp")
|
|
1474
|
+
if "set_rep" in switch_op_names:
|
|
1475
|
+
capability_set.add("rep")
|
|
1476
|
+
if "set_span" in switch_op_names:
|
|
1477
|
+
capability_set.add("span")
|
|
1478
|
+
if "set_port_security" in switch_op_names:
|
|
1479
|
+
capability_set.add("port_security")
|
|
1480
|
+
if "set_dot1x" in switch_op_names:
|
|
1481
|
+
capability_set.add("dot1x")
|
|
1482
|
+
if "set_qos_policy" in switch_op_names:
|
|
1483
|
+
capability_set.add("qos")
|
|
1484
|
+
if "set_stp" in switch_op_names:
|
|
1485
|
+
capability_set.add("stp")
|
|
1486
|
+
for op in switch_ops:
|
|
1487
|
+
if op.get("op") == "set_stp" and re.search(r"(?:rapid|rstp)", str(op.get("mode") or ""), flags=re.IGNORECASE):
|
|
1488
|
+
capability_set.add("rstp")
|
|
1489
|
+
if "set_etherchannel" in switch_op_names:
|
|
1490
|
+
capability_set.add("etherchannel")
|
|
1491
|
+
for op in switch_ops:
|
|
1492
|
+
if op.get("op") != "set_etherchannel":
|
|
1493
|
+
continue
|
|
1494
|
+
mode = str(op.get("mode") or "").lower()
|
|
1495
|
+
if mode in {"active", "passive"}:
|
|
1496
|
+
capability_set.add("lacp")
|
|
1497
|
+
if mode in {"desirable", "auto"}:
|
|
1498
|
+
capability_set.add("pagp")
|
|
1499
|
+
if "set_vtp" in switch_op_names:
|
|
1500
|
+
capability_set.add("vtp")
|
|
1501
|
+
if "set_dtp" in switch_op_names:
|
|
1502
|
+
capability_set.add("dtp")
|
|
1503
|
+
router_op_names = {str(op.get("op")) for op in router_ops}
|
|
1504
|
+
if "set_bgp_neighbor" in router_op_names:
|
|
1505
|
+
capability_set.add("bgp")
|
|
1506
|
+
if "set_snmp_community" in router_op_names:
|
|
1507
|
+
capability_set.add("snmp")
|
|
1508
|
+
if "set_netflow" in router_op_names:
|
|
1509
|
+
capability_set.add("netflow")
|
|
1510
|
+
if "set_gre_tunnel" in router_op_names:
|
|
1511
|
+
capability_set.add("gre")
|
|
1512
|
+
if "set_ppp_interface" in router_op_names:
|
|
1513
|
+
capability_set.add("ppp")
|
|
1514
|
+
if router_op_names & {"set_ipsec_transform_set", "set_crypto_map"}:
|
|
1515
|
+
capability_set.update({"ipsec", "vpn"})
|
|
1516
|
+
if "set_cbac_inspect" in router_op_names:
|
|
1517
|
+
capability_set.update({"cbac", "security_edge"})
|
|
1518
|
+
if router_op_names & {"set_zfw_zone_interface", "set_zfw_zone_pair", "set_zfw_policy"}:
|
|
1519
|
+
capability_set.update({"zfw", "security_edge"})
|
|
1520
|
+
if "set_qos_policy" not in switch_op_names:
|
|
1521
|
+
capability_set.discard("qos")
|
|
1522
|
+
capability_set.discard("server_http")
|
|
1523
|
+
capability_set.discard("server_https")
|
|
1524
|
+
if router_op_names & {"enable_ipv6_unicast_routing", "set_ipv6_address", "set_ipv6_slaac"}:
|
|
1525
|
+
capability_set.add("ipv6_slaac")
|
|
1526
|
+
if "set_dhcpv6_pool" in router_op_names:
|
|
1527
|
+
capability_set.add("dhcpv6_stateful")
|
|
1528
|
+
if "set_ospfv3_interface" in router_op_names:
|
|
1529
|
+
capability_set.add("ospfv3")
|
|
1530
|
+
if "set_eigrp_ipv6_interface" in router_op_names:
|
|
1531
|
+
capability_set.add("eigrp_ipv6")
|
|
1532
|
+
if "set_ripng_interface" in router_op_names:
|
|
1533
|
+
capability_set.add("ripng")
|
|
1534
|
+
if "set_hsrp_ipv6" in router_op_names:
|
|
1535
|
+
capability_set.add("hsrp")
|
|
1536
|
+
if "set_ospfv2_network" in router_op_names:
|
|
1537
|
+
capability_set.add("ospfv2")
|
|
1538
|
+
if "set_eigrp_ipv4_network" in router_op_names:
|
|
1539
|
+
capability_set.add("eigrp_ipv4")
|
|
1540
|
+
if "set_ripv2_network" in router_op_names:
|
|
1541
|
+
capability_set.add("ripv2")
|
|
1542
|
+
if "set_static_route" in router_op_names:
|
|
1543
|
+
capability_set.add("static_route")
|
|
1544
|
+
if any(op.get("op") == "set_static_route" and op.get("network") == "0.0.0.0" and op.get("prefix") == 0 for op in router_ops):
|
|
1545
|
+
capability_set.add("default_route")
|
|
1546
|
+
if "set_dhcp_relay" in router_op_names:
|
|
1547
|
+
capability_set.add("dhcp_relay")
|
|
1548
|
+
if "set_nat_interface" in router_op_names:
|
|
1549
|
+
capability_set.add("nat")
|
|
1550
|
+
if "set_nat_static" in router_op_names:
|
|
1551
|
+
capability_set.update({"nat", "nat_static"})
|
|
1552
|
+
if "set_pat_overload" in router_op_names:
|
|
1553
|
+
capability_set.update({"nat", "pat"})
|
|
1554
|
+
if "set_ssh_ios" in router_op_names:
|
|
1555
|
+
capability_set.update({"ssh", "ssh_ios"})
|
|
1556
|
+
if "set_ntp_server" in router_op_names:
|
|
1557
|
+
capability_set.update({"ntp", "ntp_ios"})
|
|
1558
|
+
if "set_syslog_server" in router_op_names:
|
|
1559
|
+
capability_set.update({"syslog_ios"})
|
|
1560
|
+
if router_op_names & {"set_telephony_service", "set_ephone_dn", "set_ephone", "set_dial_peer_voice"}:
|
|
1561
|
+
capability_set.update({"voip", "call_manager"})
|
|
1562
|
+
if router_op_names & {"set_ephone_dn", "set_ephone"}:
|
|
1563
|
+
capability_set.add("ip_phone")
|
|
1564
|
+
if capability_set & {"vpn", "ipsec", "gre", "ppp", "security_edge"}:
|
|
801
1565
|
network_style = network_style or "wan_security"
|
|
1566
|
+
if capability_set & {"ipv6_slaac", "dhcpv6_stateful", "dhcpv6_stateless", "ospfv3", "eigrp_ipv6", "ripng", "hsrp"}:
|
|
1567
|
+
network_style = network_style or "ipv6_routing"
|
|
1568
|
+
if capability_set & {"ospfv2", "eigrp_ipv4", "ripv2", "static_route", "default_route", "dhcp_relay", "nat_static", "nat_dynamic", "pat", "ssh_ios", "ntp_ios", "syslog_ios"}:
|
|
1569
|
+
network_style = network_style or "ipv4_routing_management"
|
|
1570
|
+
if capability_set & {"dhcp_snooping", "dai", "dot1x", "lldp", "rep", "snmp", "netflow", "span", "qos", "port_security"}:
|
|
1571
|
+
network_style = network_style or "l2_security_monitoring"
|
|
1572
|
+
if capability_set & {"bgp", "stp", "rstp", "etherchannel", "lacp", "pagp", "vtp", "dtp"}:
|
|
1573
|
+
network_style = "l2_resiliency_routing"
|
|
1574
|
+
if capability_set & {"wlc", "wpa_enterprise", "wep", "guest_wifi", "beamforming", "meraki", "cellular_5g", "bluetooth"}:
|
|
1575
|
+
network_style = "wireless_advanced"
|
|
1576
|
+
if capability_set & {"coaxial", "cable_dsl", "central_office", "cell_tower", "power_distribution", "hot_swappable", "ios_license"}:
|
|
1577
|
+
network_style = "physical_media_device"
|
|
1578
|
+
if capability_set & {"mqtt", "real_http", "real_websocket", "visual_scripting", "ptp", "profinet", "l2nat", "cyberobserver", "industrial_firewall"}:
|
|
1579
|
+
network_style = "industrial_iot"
|
|
1580
|
+
if capability_set & {"real_http", "real_websocket"}:
|
|
1581
|
+
capability_set.discard("server_http")
|
|
1582
|
+
capability_set.discard("server_https")
|
|
1583
|
+
if not (
|
|
1584
|
+
iot_ops
|
|
1585
|
+
or re.search(r"\b(?:home\s+gateway|smart\s+home|iot\s+registration|iot\s+control|iot\s+rule)\b", lowered)
|
|
1586
|
+
or re.search(r"\bregister\s+.+\s+to\b", lowered)
|
|
1587
|
+
):
|
|
1588
|
+
capability_set.discard("iot")
|
|
1589
|
+
if capability_set & {"voip", "ip_phone", "call_manager", "linksys_voice"}:
|
|
1590
|
+
network_style = "voice_collaboration"
|
|
802
1591
|
|
|
803
1592
|
capabilities = sorted(capability_set)
|
|
804
1593
|
|
|
@@ -864,4 +1653,5 @@ def parse_intent(prompt: str) -> IntentPlan:
|
|
|
864
1653
|
management_ops=management_ops,
|
|
865
1654
|
verification_ops=verification_ops,
|
|
866
1655
|
iot_ops=iot_ops,
|
|
1656
|
+
programming_ops=programming_ops,
|
|
867
1657
|
)
|