From 0d7f593127120f3f046e696133bdcc2d64074699 Mon Sep 17 00:00:00 2001 From: medusa Date: Wed, 6 Aug 2025 13:54:07 -0500 Subject: [PATCH] Add tech_docs/networking/cue_jinja.md --- tech_docs/networking/cue_jinja.md | 607 ++++++++++++++++++++++++++++++ 1 file changed, 607 insertions(+) create mode 100644 tech_docs/networking/cue_jinja.md diff --git a/tech_docs/networking/cue_jinja.md b/tech_docs/networking/cue_jinja.md new file mode 100644 index 0000000..34cac9f --- /dev/null +++ b/tech_docs/networking/cue_jinja.md @@ -0,0 +1,607 @@ +# Cisco Router Configuration Template System using CUE and Jinja2 + +I'll create a system to generate Cisco router configurations using CUE for data modeling and validation, and Jinja2 for template rendering. Here's how we can structure this: + +## 1. CUE Schema Definition (router.cue) + +```cue +package router + +#Device: { + // Basic device information + hostname: string + model: "ISR4451-X/K9" | "ISR4431" | "ASR1001-X" // etc. + serial: string + + // Location information + location: { + city: string + state: string + address1: string + address2?: string + zipcode: string + floor?: string + grid?: string + rack: string + ru: string + room: string + } + + // Device role + role: "wan-router" | "core-router" | "distribution-router" + + // Authentication + credentials: { + enableSecret: string + consolePassword: string + vtyPassword: string + tacacsKey: string + snmp: { + roCommunity: string + rwCommunity: string + users: [...{ + username: string + authType: "sha" | "md5" + authKey: string + privType: "aes" | "des" + privKey: string + access: "read" | "write" + }] + } + } + + // Network services + services: { + ntp: { + servers: [...string] + preferredServer: string + } + dns: { + servers: [...string] + domain: string + } + logging: { + hosts: [...string] + facility: string + trapLevel: "informational" | "debug" | "warning" | "error" + } + } + + // Interfaces + interfaces: { + loopback0: { + ip: string + mask: "255.255.255.255" + } + + wan: { + type: "MPLS" | "DMVPN" | "Internet" + provider: "AT&T" | "Verizon" | "Lumen" + circuitId: string + bandwidth: uint + ipAddress: string + subnetMask: string + dot1qVlan?: uint + shapingRate: uint + } + + lan: [...{ + name: string + description: string + ipAddress: string + subnetMask: string + neighbor: string + ospf: { + area: string + authKey: string + helloInt: uint + deadInt: uint + } + }] + + tunnels: [...{ + id: uint + description: string + ipAddress: string + subnetMask: string + bandwidth: uint + nhs: string + nbma: string + networkId: uint + key: uint + }] + } + + // Routing protocols + routing: { + ospf: { + processId: uint + routerId: string + areas: [...{ + id: string + authType: "message-digest" + }] + networks: [...{ + prefix: string + wildcard: string + area: string + }] + } + + bgp: { + asn: uint + routerId: string + neighbors: [...{ + ip: string + remoteAs: uint | "internal" + description: string + password?: string + routeMaps: { + in?: string + out?: string + } + }] + networks: [...{ + prefix: string + mask: string + }] + } + } + + // QoS Policy + qos: { + classes: [...{ + name: string + match: string + bandwidth: string + queueLimit?: uint + dscp: [...{ + value: uint + minTh: uint + maxTh: uint + }] + }] + shapingRate: uint + } + + // Access control + acls: { + management: [...{ + source: string + service: "ssh" | "snmp" | "ntp" | "tacacs" + direction: "in" | "out" + }] + } + + // Security policies + security: { + aaa: { + methods: [...{ + type: "login" | "enable" | "exec" + primary: "tacacs+" | "local" + fallback?: "local" | "line" + }] + accounting: [...{ + type: "exec" | "commands" | "network" + action: "start-stop" + method: "tacacs+" + }] + } + passwordPolicy: { + minLength: uint + maxFail: uint + } + } +} + +#validate: { + // Validation rules + if !strings.HasPrefix(hostname, "RTR-") { + "hostname must start with RTR-" + } + + if len(credentials.enableSecret) < 8 { + "enable secret must be at least 8 characters" + } + + // More validation rules... +} +``` + +## 2. Example Device Configuration (device.cue) + +```cue +package router + +device: #Device & { + hostname: "RTR-ATL01-WAN01" + model: "ISR4451-X/K9" + serial: "FOC12345678" + + location: { + city: "Atlanta" + state: "GA" + address1: "123 Main St" + zipcode: "30303" + rack: "RACK-A" + ru: "22" + room: "MDF-1" + } + + credentials: { + enableSecret: "$1$s3cr3t$" + consolePassword: "c0ns0l3P@ss" + vtyPassword: "vtYP@ssw0rd" + tacacsKey: "tacacsKey123" + + snmp: { + roCommunity: "publicRO" + rwCommunity: "privateRW" + users: [ + { + username: "admin" + authType: "sha" + authKey: "authKey123" + privType: "aes" + privKey: "privKey123" + access: "write" + }, + ] + } + } + + services: { + ntp: { + servers: ["135.89.160.2", "135.89.160.18"] + preferredServer: "135.89.160.18" + } + dns: { + servers: ["10.83.108.251", "10.65.72.251"] + domain: "example.com" + } + logging: { + hosts: ["10.254.254.238"] + facility: "local1" + trapLevel: "informational" + } + } + + interfaces: { + loopback0: { + ip: "10.255.255.1" + mask: "255.255.255.255" + } + + wan: { + type: "MPLS" + provider: "AT&T" + circuitId: "ABC123456" + bandwidth: 1000 + ipAddress: "172.30.65.102" + subnetMask: "255.255.255.252" + dot1qVlan: 249 + shapingRate: 1000 + } + + lan: [ + { + name: "GigabitEthernet0/0/1" + description: "Core Switch Connection" + ipAddress: "10.1.1.1" + subnetMask: "255.255.255.254" + neighbor: "CORE-ATL01" + ospf: { + area: "0" + authKey: "ospfKey123" + helloInt: 1 + deadInt: 4 + } + }, + ] + + tunnels: [ + { + id: 11 + description: "DMVPN-MPLS to Charlotte" + ipAddress: "172.30.4.2" + subnetMask: "255.255.254.0" + bandwidth: 1000 + nhs: "172.30.4.1" + nbma: "172.30.65.101" + networkId: 200 + key: 1002 + }, + ] + } + + routing: { + ospf: { + processId: 1 + routerId: "10.255.255.1" + areas: [ + {id: "0", authType: "message-digest"} + ] + networks: [ + {prefix: "10.1.1.0", wildcard: "0.0.0.1", area: "0"}, + {prefix: "10.255.255.1", wildcard: "0.0.0.0", area: "0"} + ] + } + + bgp: { + asn: 65401 + routerId: "10.255.255.1" + neighbors: [ + { + ip: "172.30.4.1" + remoteAs: "internal" + description: "DMVPN Headend" + routeMaps: { + in: "BGP_INBOUND_FROM_DMVPN" + out: "BGP_OUTBOUND_TO_DMVPN" + } + }, + ] + networks: [ + {prefix: "10.1.1.0", mask: "255.255.255.0"}, + {prefix: "10.255.255.1", mask: "255.255.255.255"} + ] + } + } + + qos: { + classes: [ + { + name: "VOICE_PAYLOAD" + match: "dscp ef cs5" + bandwidth: "priority percent 30" + }, + { + name: "INTERACTIVE_VIDEO" + match: "dscp cs4 af41 af42 af43" + bandwidth: "bandwidth remaining percent 15" + queueLimit: 300 + dscp: [ + {value: 32, minTh: 300, maxTh: 300}, + {value: 34, minTh: 150, maxTh: 250}, + ] + }, + ] + shapingRate: 1000 + } + + security: { + aaa: { + methods: [ + {type: "login", primary: "tacacs+", fallback: "local"}, + {type: "enable", primary: "tacacs+", fallback: "enable"}, + ] + accounting: [ + {type: "exec", action: "start-stop", method: "tacacs+"}, + {type: "commands", action: "start-stop", method: "tacacs+"}, + ] + } + passwordPolicy: { + minLength: 8 + maxFail: 5 + } + } +} +``` + +## 3. Jinja2 Template (cisco_template.j2) + +```jinja2 +! Generated from template on {{ now() }} +! Device: {{ device.hostname }} +! Location: {{ device.location.city }}, {{ device.location.state }} + +hostname {{ device.hostname }} +! +boot system bootflash:isr4400-universalk9.16.09.05.SPA.bin +! +service password-encryption +no ip domain lookup +ip domain name {{ device.services.dns.domain }} +! +{% for server in device.services.dns.servers %} +ip name-server {{ server }} +{% endfor %} +! +! AAA Configuration +aaa new-model +aaa authentication attempts login {{ device.security.passwordPolicy.maxFail }} +! +{% for method in device.security.aaa.methods %} +aaa authentication {{ method.type }} default group {{ method.primary }}{% if method.fallback %} {{ method.fallback }}{% endif %} +{% endfor %} +! +{% for accounting in device.security.aaa.accounting %} +aaa accounting {{ accounting.type }} default {{ accounting.action }} group {{ accounting.method }} +{% endfor %} +! +! SNMP Configuration +{% for user in device.credentials.snmp.users %} +snmp-server user {{ user.username }} {{ user.access }} v3 auth {{ user.authType }} {{ user.authKey }} priv {{ user.privType }} {{ user.privKey }} +{% endfor %} +! +! NTP Configuration +ntp source Loopback0 +ntp server {{ device.services.ntp.preferredServer }} prefer +{% for server in device.services.ntp.servers %} +{% if server != device.services.ntp.preferredServer %} +ntp server {{ server }} +{% endif %} +{% endfor %} +! +! Logging Configuration +logging source-interface Loopback0 +logging trap {{ device.services.logging.trapLevel }} +logging facility {{ device.services.logging.facility }} +{% for host in device.services.logging.hosts %} +logging host {{ host }} +{% endfor %} +! +! Interface Configuration +interface Loopback0 + description Loopback Address + ip address {{ device.interfaces.loopback0.ip }} {{ device.interfaces.loopback0.mask }} +! +interface GigabitEthernet0/0/0 + description {{ device.interfaces.wan.provider }} MPLS - {{ device.interfaces.wan.circuitId }} + bandwidth {{ device.interfaces.wan.bandwidth }} + no ip address +! +interface GigabitEthernet0/0/0.{{ device.interfaces.wan.dot1qVlan }} + description {{ device.interfaces.wan.provider }} MPLS - {{ device.interfaces.wan.circuitId }} + encapsulation dot1Q {{ device.interfaces.wan.dot1qVlan }} + ip address {{ device.interfaces.wan.ipAddress }} {{ device.interfaces.wan.subnetMask }} + service-policy output SHAPE_{{ device.interfaces.wan.shapingRate }}M +! +{% for lan in device.interfaces.lan %} +interface {{ lan.name }} + description {{ lan.description }} + ip address {{ lan.ipAddress }} {{ lan.subnetMask }} + ip ospf authentication message-digest + ip ospf message-digest-key 1 md5 {{ lan.ospf.authKey }} + ip ospf hello-interval {{ lan.ospf.helloInt }} + ip ospf dead-interval {{ lan.ospf.deadInt }} +! +{% endfor %} +! +{% for tunnel in device.interfaces.tunnels %} +interface Tunnel{{ tunnel.id }} + description {{ tunnel.description }} + bandwidth {{ tunnel.bandwidth }} + ip address {{ tunnel.ipAddress }} {{ tunnel.subnetMask }} + ip nhrp network-id {{ tunnel.networkId }} + ip nhrp nhs {{ tunnel.nhs }} nbma {{ tunnel.nbma }} multicast + tunnel key {{ tunnel.key }} +! +{% endfor %} +! +! OSPF Configuration +router ospf {{ device.routing.ospf.processId }} + router-id {{ device.routing.ospf.routerId }} +{% for area in device.routing.ospf.areas %} + area {{ area.id }} authentication {{ area.authType }} +{% endfor %} +{% for net in device.routing.ospf.networks %} + network {{ net.prefix }} {{ net.wildcard }} area {{ net.area }} +{% endfor %} +! +! BGP Configuration +router bgp {{ device.routing.bgp.asn }} + bgp router-id {{ device.routing.bgp.routerId }} +{% for net in device.routing.bgp.networks %} + network {{ net.prefix }} mask {{ net.mask }} +{% endfor %} +{% for neighbor in device.routing.bgp.neighbors %} + neighbor {{ neighbor.ip }} remote-as {{ neighbor.remoteAs }} + neighbor {{ neighbor.ip }} description {{ neighbor.description }} +{% if neighbor.routeMaps.in %} neighbor {{ neighbor.ip }} route-map {{ neighbor.routeMaps.in }} in{% endif %} +{% if neighbor.routeMaps.out %} neighbor {{ neighbor.ip }} route-map {{ neighbor.routeMaps.out }} out{% endif %} +{% if neighbor.password %} neighbor {{ neighbor.ip }} password {{ neighbor.password }}{% endif %} +{% endfor %} +! +! QoS Configuration +policy-map WAN_QOS_OUTBOUND +{% for class in device.qos.classes %} + class {{ class.name }} + {{ class.bandwidth }} +{% if class.queueLimit %} queue-limit {{ class.queueLimit }} packets{% endif %} +{% for dscp in class.dscp %} + random-detect dscp {{ dscp.value }} {{ dscp.minTh }} {{ dscp.maxTh }} +{% endfor %} +{% endfor %} +! +policy-map SHAPE_{{ device.qos.shapingRate }}M + class class-default + shape average {{ device.qos.shapingRate }}000000 + service-policy WAN_QOS_OUTBOUND +! +! Banner +banner motd ^ +******************************************************************************** +* Warning Notice * +* * +* This system is restricted solely to authorized users for legitimate * +* business purposes only. * +******************************************************************************** +^ +! +line con 0 + password {{ device.credentials.consolePassword }} + login local +! +line vty 0 15 + password {{ device.credentials.vtyPassword }} + login local + transport input ssh +! +end +``` + +## 4. Generation Script (generate.py) + +```python +#!/usr/bin/env python3 +import argparse +import jinja2 +import json +import datetime +from cue import load + +def now(): + return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + +def main(): + parser = argparse.ArgumentParser(description="Generate Cisco configurations from CUE definitions") + parser.add_argument("cue_file", help="Input CUE file with device definition") + parser.add_argument("template_file", help="Jinja2 template file") + parser.add_argument("-o", "--output", help="Output file", default="output.cfg") + + args = parser.parse_args() + + # Load CUE data + cue_data = load.cue(args.cue_file) + device = cue_data["device"] + + # Set up Jinja2 environment + env = jinja2.Environment(loader=jinja2.FileSystemLoader('.')) + env.globals['now'] = now + template = env.get_template(args.template_file) + + # Render template + config = template.render(device=device) + + # Write output + with open(args.output, 'w') as f: + f.write(config) + + print(f"Configuration generated and saved to {args.output}") + +if __name__ == "__main__": + main() +``` + +## 5. Workflow Explanation + +1. **Define the Schema**: The CUE schema (`router.cue`) defines all possible configuration options with types and constraints. + +2. **Create Device Config**: The device-specific configuration (`device.cue`) uses the schema to define a concrete device configuration. + +3. **Validate**: The CUE engine validates the device configuration against the schema before processing. + +4. **Render Template**: The Jinja2 template (`cisco_template.j2`) uses the validated device configuration to generate the Cisco config. + +5. **Generate Config**: The Python script (`generate.py`) ties everything together, loading the CUE data and rendering the template. + +## Benefits of This Approach + +1. **Validation**: CUE ensures all configurations are valid before template rendering. +2. **Reusability**: Templates can be reused across many devices. +3. **Maintainability**: Changes to the schema automatically validate all configurations. +4. **Documentation**: The schema serves as documentation for all configurable options. +5. **Version Control**: All components can be version controlled separately. + +Would you like me to elaborate on any particular aspect of this system? \ No newline at end of file