Update tech_docs/networking/iac_github_project.md
This commit is contained in:
@@ -1,6 +1,634 @@
|
|||||||
Below is a vendor-agnostic, scalable-template review written in “meta-config” form.
|
Below is a vendor-agnostic, scalable-template review written in “meta-config” form.
|
||||||
It is intentionally abstract (no literal values, no vendor CLI) so it can be mechanically translated to any NOS or rendered by an automation pipeline.
|
It is intentionally abstract (no literal values, no vendor CLI) so it can be mechanically translated to any NOS or rendered by an automation pipeline.
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
1. Meta-Template Structure
|
||||||
|
--------------------------------------------------------
|
||||||
|
┌─ object: device
|
||||||
|
│ ├─ role: head-end-dmvpn-hub
|
||||||
|
│ ├─ platform: <vendor-agnostic>
|
||||||
|
│ └─ lifecycle: golden-template → instance-template → device-config
|
||||||
|
└─
|
||||||
|
|
||||||
|
Each stanza below is a YAML-ish block that can be turned into:
|
||||||
|
- Jinja2 / Ansible variables
|
||||||
|
- Terraform schema
|
||||||
|
- OpenConfig YANG
|
||||||
|
- TTP/TTK parser
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
2. Inventory & Naming
|
||||||
|
--------------------------------------------------------
|
||||||
|
inventory:
|
||||||
|
site_id: "{{ site_id }}" # AAA-BBB-CCC-NNN
|
||||||
|
function: headend
|
||||||
|
routing_domain: "{{ rd_index }}"
|
||||||
|
hostname_pattern: "{{ site_id }}-{{ function }}-{{ sequence }}"
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
3. OS / Image Management
|
||||||
|
--------------------------------------------------------
|
||||||
|
image:
|
||||||
|
golden_version: "{{ lookup('golden_db', platform) }}"
|
||||||
|
fallback_version: "{{ golden_version | fallback }}"
|
||||||
|
boot_order: [primary, secondary, usb]
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
4. Global Service Knobs
|
||||||
|
--------------------------------------------------------
|
||||||
|
global:
|
||||||
|
service:
|
||||||
|
tcp_keepalives: { in: true, out: true }
|
||||||
|
timestamps: { debug: msec, log: msec, tz: local }
|
||||||
|
password_encryption: true
|
||||||
|
sequence_numbers: true
|
||||||
|
counters_max_age: 10
|
||||||
|
dhcp: false
|
||||||
|
pad: false
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
5. Security Baseline
|
||||||
|
--------------------------------------------------------
|
||||||
|
security:
|
||||||
|
auth_failure_rate: 3
|
||||||
|
password_policy:
|
||||||
|
min_length: 8
|
||||||
|
complexity: high
|
||||||
|
aaa:
|
||||||
|
method_order: [tacacs, local]
|
||||||
|
accounting: start-stop
|
||||||
|
sources:
|
||||||
|
- { ip: "{{ tacacs_vip }}", vrf: mgmt }
|
||||||
|
secrets:
|
||||||
|
enable: "{{ vault.encrypted(enable_secret) }}"
|
||||||
|
snmp:
|
||||||
|
version: 3
|
||||||
|
auth: sha
|
||||||
|
priv: aes-128
|
||||||
|
acl: "{{ snmp_acl }}"
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
6. VRF & Loopback Plan
|
||||||
|
--------------------------------------------------------
|
||||||
|
vrfs:
|
||||||
|
- name: mgmt
|
||||||
|
rd: "{{ site_id }}:1"
|
||||||
|
interfaces: [MgmtEth0/0/0]
|
||||||
|
- name: dmvpn
|
||||||
|
rd: "{{ site_id }}:2"
|
||||||
|
interfaces: [Loopback-DMVPN, Tunnel-*]
|
||||||
|
|
||||||
|
loopbacks:
|
||||||
|
- name: system
|
||||||
|
vrf: default
|
||||||
|
mask: /32
|
||||||
|
- name: tunnel_source
|
||||||
|
vrf: dmvpn
|
||||||
|
mask: /32
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
7. Underlay Interfaces
|
||||||
|
--------------------------------------------------------
|
||||||
|
underlay:
|
||||||
|
uplinks:
|
||||||
|
- id: 1
|
||||||
|
type: p2p
|
||||||
|
media: ethernet
|
||||||
|
mtu: 9216
|
||||||
|
vrf: default
|
||||||
|
ospf: { area: 0, auth: md5, hello: 1, dead: 4 }
|
||||||
|
- id: 2
|
||||||
|
type: p2p
|
||||||
|
media: ethernet
|
||||||
|
mtu: 9216
|
||||||
|
vrf: dmvpn
|
||||||
|
ospf: { area: 0, auth: md5, hello: 1, dead: 4 }
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
8. Overlay (DMVPN) Definition
|
||||||
|
--------------------------------------------------------
|
||||||
|
overlay:
|
||||||
|
type: dmvpn-hub
|
||||||
|
tunnel_ifs:
|
||||||
|
- id: 1
|
||||||
|
src_loopback: tunnel_source
|
||||||
|
vrf: dmvpn
|
||||||
|
mtu: 1400
|
||||||
|
tcp_mss: 1360
|
||||||
|
nhrp:
|
||||||
|
auth: "{{ nhrp_key }}"
|
||||||
|
net_id: "{{ site_id }}"
|
||||||
|
holdtime: 600
|
||||||
|
shortcut: true
|
||||||
|
redirect: true
|
||||||
|
ipsec:
|
||||||
|
profile: dmvpn_profile
|
||||||
|
transform: { enc: aes256-gcm, pfs: group20 }
|
||||||
|
bgp_listen_range: "{{ tunnel_net }}"
|
||||||
|
bgp_peer_group:
|
||||||
|
name: spokes
|
||||||
|
asn: "{{ bgp_asn }}"
|
||||||
|
rr_client: true
|
||||||
|
next_hop_self: true
|
||||||
|
send_default: true
|
||||||
|
max_peers: "{{ spoke_limit }}"
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
9. QoS Framework
|
||||||
|
--------------------------------------------------------
|
||||||
|
qos:
|
||||||
|
classifier:
|
||||||
|
- { name: voice, dscp: ef }
|
||||||
|
- { name: interactive_vid, dscp: [af41,af42,af43] }
|
||||||
|
- { name: critical_data, dscp: [af31,af32,af33] }
|
||||||
|
- { name: business_data, dscp: [af21,af22,af23] }
|
||||||
|
- { name: bulk_data, dscp: [af11,af12,af13] }
|
||||||
|
- { name: scavenger, dscp: cs1 }
|
||||||
|
- { name: net_mgmt, dscp: cs2 }
|
||||||
|
shaper:
|
||||||
|
- parent: physical
|
||||||
|
cir: "{{ circuit_bw }}"
|
||||||
|
child_policy: per_class
|
||||||
|
per_class:
|
||||||
|
voice: { priority_pct: 30 }
|
||||||
|
interactive_vid: { bw_pct: 15, wred: true }
|
||||||
|
critical_data: { bw_pct: 20, wred: true }
|
||||||
|
business_data: { bw_pct: 25, wred: true }
|
||||||
|
bulk_data: { bw_pct: 10, wred: true }
|
||||||
|
scavenger: { bw_pct: 5, wred: true }
|
||||||
|
class_default: { bw_pct: 20, fair_queue: true }
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
10. NetFlow / Telemetry
|
||||||
|
--------------------------------------------------------
|
||||||
|
telemetry:
|
||||||
|
exporter:
|
||||||
|
- { dst: "{{ collector_vip }}", vrf: mgmt, dscp: af21, proto: udp/9996 }
|
||||||
|
cache:
|
||||||
|
active_timeout: 60
|
||||||
|
inactive_timeout: 15
|
||||||
|
fields:
|
||||||
|
- { match: [ipv4_src, ipv4_dst, tos, proto, port_src, port_dst, direction] }
|
||||||
|
- { collect: [bytes, pkts, first_seen, last_seen, next_hop] }
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
11. Routing Policy
|
||||||
|
--------------------------------------------------------
|
||||||
|
policy:
|
||||||
|
ospf:
|
||||||
|
areas:
|
||||||
|
0: { auth: md5, type: p2p_only }
|
||||||
|
default_originate: true
|
||||||
|
bgp:
|
||||||
|
local_as: "{{ bgp_asn }}"
|
||||||
|
communities:
|
||||||
|
- { name: blackhole, pattern: "65400:666" }
|
||||||
|
- { name: transit_nyc, pattern: "65400:1111" }
|
||||||
|
- { name: transit_clt, pattern: "65400:2222" }
|
||||||
|
- { name: transit_brm, pattern: "65400:3333" }
|
||||||
|
route_maps:
|
||||||
|
- { name: deny_default, seq: 10, action: deny, match: prefix=0.0.0.0/0 }
|
||||||
|
- { name: spokes_in, seq: 20, action: permit }
|
||||||
|
- { name: spokes_out, seq: 10, action: permit, set: [community=local_site] }
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
12. Monitoring & SLA
|
||||||
|
--------------------------------------------------------
|
||||||
|
sla:
|
||||||
|
icmp_probes:
|
||||||
|
- { target: "{{ anycast_gw }}", src: tunnel_1, freq: 180, owner: dmvpn }
|
||||||
|
logging:
|
||||||
|
hosts:
|
||||||
|
- { ip: "{{ loghost1 }}", vrf: mgmt, facility: local1 }
|
||||||
|
- { ip: "{{ loghost2 }}", vrf: mgmt, facility: local1 }
|
||||||
|
levels:
|
||||||
|
- { trap: informational, origin_id: loopback }
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
13. Hardening & ACLs
|
||||||
|
--------------------------------------------------------
|
||||||
|
acl:
|
||||||
|
mgmt_plane:
|
||||||
|
- { src: "{{ mgmt_net }}", dst: device, proto: ssh }
|
||||||
|
- { src: "{{ mgmt_net }}", dst: device, proto: snmp }
|
||||||
|
control_plane:
|
||||||
|
- { src: "{{ spoke_net }}", dst: device, proto: ipsec }
|
||||||
|
- { src: "{{ spoke_net }}", dst: device, proto: nhrp }
|
||||||
|
data_plane:
|
||||||
|
- { name: undesirable_udp, action: drop, rate: 8kbps }
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
14. Automation Hooks
|
||||||
|
--------------------------------------------------------
|
||||||
|
automation:
|
||||||
|
pre_checks:
|
||||||
|
- validate_image_md5
|
||||||
|
- validate_crypto_modulus
|
||||||
|
post_checks:
|
||||||
|
- bgp_established
|
||||||
|
- ipsec_sa_count
|
||||||
|
- sla_probe_up
|
||||||
|
drift_detection:
|
||||||
|
- periodic: "{{ 24h }}"
|
||||||
|
- source_of_truth: git
|
||||||
|
- remediation: on_change
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
15. Extensibility Notes
|
||||||
|
--------------------------------------------------------
|
||||||
|
- All IP literals live in inventory or vault; template never embeds them.
|
||||||
|
- All BGP communities, NHRP keys, SNMP strings are vaulted and templated.
|
||||||
|
- QoS class names & DSCP values are constants; per-link CIR is dynamic.
|
||||||
|
- The entire config is rendered in three artifacts:
|
||||||
|
1. golden-template.yaml (this document)
|
||||||
|
2. per-site variables (YAML or JSON)
|
||||||
|
3. vendor renderer (Jinja2, YDK, NAPALM, etc.)
|
||||||
|
|
||||||
|
This single source-of-truth can therefore drive Cisco IOS-XE, Juniper vSRX, Aruba CX, OpenBSD, or a virtual container image without further human touch.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Yes—the structure you showed is **exactly** the next logical layer under the vendor-agnostic “meta-template” I sketched.
|
||||||
|
Think of the meta-template as the **contract** (what must exist, how it behaves), and your `config_components/` tree as the **concrete implementation** that an automation engine (Ansible, Salt, Nornir, etc.) consumes to stamp out per-device artifacts.
|
||||||
|
|
||||||
|
Below is a mapping that keeps the two layers coherent and keeps every artifact reusable across chassis types or vendors.
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
1. Directory ↔ Meta-Template Mapping
|
||||||
|
--------------------------------------------------------
|
||||||
|
config_components/
|
||||||
|
├── core_settings/
|
||||||
|
│ ├── 00_licensing.j2 ← global.image (golden version, feature licences)
|
||||||
|
│ ├── 10_system_settings.j2 ← global.service knobs, hostname, NTP, banner
|
||||||
|
│ └── 20_aaa.j2 ← security.aaa, tacacs sources, local users
|
||||||
|
├── network_services/
|
||||||
|
│ ├── 30_vlans.j2 ← vrfs.* (L3 VNIs) + any underlay VLANs
|
||||||
|
│ └── 40_routing.j2 ← policy.ospf, policy.bgp, route-maps, NHRP
|
||||||
|
├── interfaces/
|
||||||
|
│ ├── 50_port_profiles/
|
||||||
|
│ │ ├── access_port.j2 ← not used on a DMVPN hub, but kept for reuse
|
||||||
|
│ │ └── trunk_port.j2 ← likewise reusable
|
||||||
|
│ └── 60_interface_assignments.j2
|
||||||
|
│ ← underlay.*, overlay.tunnel_ifs, QoS service-policy attachment
|
||||||
|
├── policies/
|
||||||
|
│ ├── 70_qos.j2 ← qos.classifier + qos.shaper + qos.per_class
|
||||||
|
│ └── 80_access_lists.j2 ← acl.mgmt_plane + acl.control_plane + ACL fragments
|
||||||
|
└── data/
|
||||||
|
├── site-01.yml ← inventory + per-site variable overlay
|
||||||
|
└── site-02.yml
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
2. Data-file Inheritance Model
|
||||||
|
--------------------------------------------------------
|
||||||
|
data/site-XX.yml
|
||||||
|
├── inherits: "dmvpn_hub.yml" # shared across all hubs
|
||||||
|
├── overlays:
|
||||||
|
│ ├── hostname: "{{ site_id }}-hub-01"
|
||||||
|
│ ├── tunnel_net: "10.130.108.0/25"
|
||||||
|
│ ├── circuit_bw: "1 Gbps"
|
||||||
|
│ └── bgp_asn: 65400
|
||||||
|
└── secrets: "@vault/dmvpn/{{ site_id }}"
|
||||||
|
|
||||||
|
The meta-template (YAML in my previous reply) becomes the **schema** that every data file must satisfy; the J2 files become the **renderer**.
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
3. Rendering Flow
|
||||||
|
--------------------------------------------------------
|
||||||
|
1. Inventory script loads `data/site-01.yml`
|
||||||
|
2. Validates against meta-template schema (pydantic, jsonschema, or yamale)
|
||||||
|
3. Renders each `.j2` file in numerical order into a single running-config stanza
|
||||||
|
4. Post-render hooks (linting, drift check, push via Nornir/Napalm)
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
4. Vendor-Neutral Abstraction Tips
|
||||||
|
--------------------------------------------------------
|
||||||
|
- Keep **no literals** inside `.j2`; every value is `{{ var }}` or `{{ var | default }}`.
|
||||||
|
- Use **macro includes** for multi-vendor syntax:
|
||||||
|
`{% import 'vendor/ios.j2' as syntax %}` vs `{% import 'vendor/junos.j2' %}`
|
||||||
|
- Store **feature flags** in the data file so the same J2 can be skipped on platforms that lack a capability (e.g., crypto-isakmp on Cisco, security-ike on JunOS).
|
||||||
|
- If a component is unused (e.g., access_port.j2 on an ASR), the data file simply omits that key and the macro renders nothing.
|
||||||
|
|
||||||
|
--------------------------------------------------------
|
||||||
|
5. Quick Starter Skeleton
|
||||||
|
--------------------------------------------------------
|
||||||
|
config_components/
|
||||||
|
├── _macros/
|
||||||
|
│ ├── vendor/
|
||||||
|
│ │ ├── ios.j2
|
||||||
|
│ │ └── junos.j2
|
||||||
|
├── core_settings/
|
||||||
|
│ └── … (as above)
|
||||||
|
├── network_services/
|
||||||
|
│ └── …
|
||||||
|
└── data/
|
||||||
|
├── dmvpn_hub.yml ← shared variables & schema
|
||||||
|
├── site-01.yml ← per-site override
|
||||||
|
└── site-02.yml
|
||||||
|
|
||||||
|
With this, the **meta-template** remains the single source of truth, your **config_components/** tree remains tidy, and any future chassis or vendor swap is a one-line change in the renderer macro—not a rewrite of the logic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Below is the **vendor-agnostic YAML fragment** that maps every CLI-level knob you listed to a single, automation-friendly variable path.
|
||||||
|
No literals, no vendor syntax—just the contract the Jinja2 (or any renderer) will later expand into IOS, JunOS, EOS, etc.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
global_system:
|
||||||
|
|
||||||
|
hostname: "{{ inventory_hostname }}"
|
||||||
|
|
||||||
|
boot:
|
||||||
|
image: "{{ golden_image | default('unset') }}"
|
||||||
|
config_register: "{{ config_register | default('0x2102') }}"
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
enable: "{{ vault.encrypted(enable_secret) }}"
|
||||||
|
|
||||||
|
clock:
|
||||||
|
timezone: "{{ tz_name | default('UTC') }}"
|
||||||
|
offset: "{{ tz_offset | default(0) }}"
|
||||||
|
summer_time:
|
||||||
|
rules: "{{ summer_rules | default('none') }}" # e.g., 'usa', 'eu', 'recurring'
|
||||||
|
|
||||||
|
domain:
|
||||||
|
name: "{{ dns_domain }}"
|
||||||
|
|
||||||
|
name_servers: "{{ dns_servers | default([]) }}"
|
||||||
|
|
||||||
|
logging:
|
||||||
|
buffered:
|
||||||
|
size: "{{ log_buffer_bytes | default(1048576) }}"
|
||||||
|
console: "{{ log_console | default(false) }}"
|
||||||
|
monitor: "{{ log_monitor | default('informational') }}"
|
||||||
|
hosts:
|
||||||
|
- { ip: "{{ log_host_1 }}", vrf: "{{ log_vrf | default('mgmt') }}",
|
||||||
|
proto: "{{ log_proto | default('udp') }}", port: "{{ log_port | default(514) }}" }
|
||||||
|
- { ip: "{{ log_host_2 }}", vrf: "{{ log_vrf | default('mgmt') }}",
|
||||||
|
proto: "{{ log_proto | default('udp') }}", port: "{{ log_port | default(514) }}" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage notes:
|
||||||
|
- `vault.encrypted(...)` is a Jinja2 filter that pulls the secret from the vault at render time.
|
||||||
|
- `inventory_hostname` is injected by Ansible/Nornir.
|
||||||
|
- The lists (`dns_servers`, `hosts`) are idempotent arrays—renderers can loop (`{% for %}`) to generate one CLI stanza per element.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Below is a **deployment-ready YAML scaffold** that expands every knob in your latest checklist into **parameter-only** form—no literals, no CLI, just the variables an automation engine will resolve at render time.
|
||||||
|
Copy this into `data/<site>.yml`, fill in the blanks, and feed it to the Jinja2 fragments you already keep in `config_components/`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
###############################################################################
|
||||||
|
# AAA
|
||||||
|
###############################################################################
|
||||||
|
aaa:
|
||||||
|
new_model: true
|
||||||
|
tacacs:
|
||||||
|
servers:
|
||||||
|
- { ip: "{{ tacacs1 }}", vrf: "{{ mgmt_vrf }}", key: "{{ vault.tacacs_key }}" }
|
||||||
|
- { ip: "{{ tacacs2 }}", vrf: "{{ mgmt_vrf }}", key: "{{ vault.tacacs_key }}" }
|
||||||
|
group_name: "{{ tacacs_group | default('GTAC') }}"
|
||||||
|
auth_login_default: "{{ aaa_login_method | default(['group tacacs', 'local']) }}"
|
||||||
|
auth_enable_default: "{{ aaa_enable_method | default(['group tacacs', 'enable']) }}"
|
||||||
|
auth_commands_15: "{{ aaa_cmd_method | default(['group tacacs', 'local']) }}"
|
||||||
|
accounting_commands_15:
|
||||||
|
type: "{{ acct_type | default('start-stop') }}"
|
||||||
|
group: "{{ acct_group | default('GTAC') }}"
|
||||||
|
local_users:
|
||||||
|
- { name: "{{ local_admin }}", priv: 15, secret: "{{ vault.local_secret }}" }
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# VRFs
|
||||||
|
###############################################################################
|
||||||
|
vrfs:
|
||||||
|
- name: "{{ mgmt_vrf }}"
|
||||||
|
rd: "{{ site_id }}:1"
|
||||||
|
af: [ipv4, ipv6]
|
||||||
|
- name: "{{ dmvpn_vrf }}"
|
||||||
|
rd: "{{ site_id }}:2"
|
||||||
|
af: [ipv4]
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# CRYPTO
|
||||||
|
###############################################################################
|
||||||
|
crypto:
|
||||||
|
keyring:
|
||||||
|
name: "{{ keyring_name }}"
|
||||||
|
vrf: "{{ dmvpn_vrf }}"
|
||||||
|
psk_list:
|
||||||
|
- { ip: "{{ spoke_range }}", key: "{{ vault.psk }}" }
|
||||||
|
isakmp:
|
||||||
|
policy:
|
||||||
|
id: "{{ isakmp_policy_id | default(10) }}"
|
||||||
|
encr: "{{ isakmp_encr | default('aes') }}"
|
||||||
|
auth: "{{ isakmp_auth | default('pre-share') }}"
|
||||||
|
group: "{{ isakmp_group | default('14') }}"
|
||||||
|
ipsec:
|
||||||
|
transform:
|
||||||
|
name: "{{ ipsec_transform }}"
|
||||||
|
esp: "{{ ipsec_esp | default('esp-aes 256 esp-sha-hmac') }}"
|
||||||
|
mode: "{{ ipsec_mode | default('transport') }}"
|
||||||
|
profile:
|
||||||
|
name: "{{ ipsec_profile }}"
|
||||||
|
pfs: "{{ ipsec_pfs | default(false) }}"
|
||||||
|
idle_time: "{{ ipsec_idle | default(60) }}"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# DMVPN TUNNEL
|
||||||
|
###############################################################################
|
||||||
|
tunnel:
|
||||||
|
id: "{{ tunnel_id | default(1) }}"
|
||||||
|
ip: "{{ tunnel_ip }}"
|
||||||
|
mask: "{{ tunnel_mask }}"
|
||||||
|
source: "{{ tunnel_source_intf }}"
|
||||||
|
mode: "{{ tunnel_mode | default('gre multipoint') }}"
|
||||||
|
key: "{{ tunnel_key }}"
|
||||||
|
vrf: "{{ dmvpn_vrf }}"
|
||||||
|
ipsec_profile: "{{ ipsec_profile }}"
|
||||||
|
nhrp:
|
||||||
|
net_id: "{{ nhrp_net_id }}"
|
||||||
|
auth: "{{ nhrp_key }}"
|
||||||
|
server_only: true
|
||||||
|
shortcut: true
|
||||||
|
redirect: true
|
||||||
|
tcp_mss: "{{ tunnel_tcp_mss | default(1360) }}"
|
||||||
|
qos_pre_classify: true
|
||||||
|
output_policy: "{{ qos_policy_out | default('WAN_QOS_OUTBOUND') }}"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ROUTING – OSPF
|
||||||
|
###############################################################################
|
||||||
|
ospf:
|
||||||
|
- pid: "{{ ospf_pid_uplink | default(1) }}"
|
||||||
|
vrf: "{{ mgmt_vrf }}"
|
||||||
|
rid: "{{ loopback0_ip }}"
|
||||||
|
ref_bw: "{{ ospf_ref_bw | default(100000) }}"
|
||||||
|
passive_default: true
|
||||||
|
areas:
|
||||||
|
- id: "{{ ospf_area_uplink | default(0) }}"
|
||||||
|
auth: message-digest
|
||||||
|
networks:
|
||||||
|
- { prefix: "{{ uplink_net }}", mask: "{{ uplink_wildcard }}", passive: false }
|
||||||
|
- pid: "{{ ospf_pid_dmvpn | default(22) }}"
|
||||||
|
vrf: "{{ dmvpn_vrf }}"
|
||||||
|
rid: "{{ tunnel_rid }}"
|
||||||
|
areas:
|
||||||
|
- id: "{{ ospf_area_dmvpn | default(0) }}"
|
||||||
|
auth: message-digest
|
||||||
|
networks:
|
||||||
|
- { prefix: "{{ tunnel_net }}", mask: "{{ tunnel_wildcard }}", passive: false }
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ROUTING – BGP
|
||||||
|
###############################################################################
|
||||||
|
bgp:
|
||||||
|
asn: "{{ bgp_asn }}"
|
||||||
|
rid: "{{ loopback0_ip }}"
|
||||||
|
log_changes: true
|
||||||
|
neighbors:
|
||||||
|
- { ip: "{{ ebgp_peer1 }}", remote_as: "{{ ebgp_as1 }}", desc: "To-VPN-Core-1",
|
||||||
|
ebgp_multihop: 2, update_source: "{{ loopback0_intf }}", pw: "{{ vault.bgp_pw }}" }
|
||||||
|
- { ip: "{{ ebgp_peer2 }}", remote_as: "{{ ebgp_as2 }}", desc: "To-VPN-Core-2",
|
||||||
|
ebgp_multihop: 2, update_source: "{{ loopback0_intf }}", pw: "{{ vault.bgp_pw }}" }
|
||||||
|
listen:
|
||||||
|
range: "{{ spoke_range }}"
|
||||||
|
peer_group: "{{ spoke_pg | default('REMOTE-OFFICE') }}"
|
||||||
|
limit: "{{ spoke_limit | default(300) }}"
|
||||||
|
af_ipv4:
|
||||||
|
networks:
|
||||||
|
- { prefix: "{{ local_summary_net }}", mask: "{{ local_summary_mask }}" }
|
||||||
|
peer_policies:
|
||||||
|
- group: "{{ spoke_pg }}"
|
||||||
|
activate: true
|
||||||
|
rr_client: true
|
||||||
|
next_hop_self_all: true
|
||||||
|
default_originate: true
|
||||||
|
soft_reconfig: true
|
||||||
|
route_map_in: "{{ rm_spoke_in }}"
|
||||||
|
route_map_out: "{{ rm_spoke_out }}"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# QOS
|
||||||
|
###############################################################################
|
||||||
|
qos:
|
||||||
|
class_maps:
|
||||||
|
- name: VOICE
|
||||||
|
match: [dscp ef]
|
||||||
|
- name: INTERACTIVE_VIDEO
|
||||||
|
match: [dscp cs4, dscp af41-af43]
|
||||||
|
- name: CRITICAL_DATA
|
||||||
|
match: [dscp af31-af33]
|
||||||
|
- name: BUSINESS_DATA
|
||||||
|
match: [dscp af21-af23]
|
||||||
|
- name: BULK_DATA
|
||||||
|
match: [dscp af11-af13]
|
||||||
|
- name: SCAVENGER
|
||||||
|
match: [dscp cs1]
|
||||||
|
policy_maps:
|
||||||
|
- name: "{{ qos_policy_out }}"
|
||||||
|
classes:
|
||||||
|
- { name: VOICE, priority_pct: 30 }
|
||||||
|
- { name: INTERACTIVE_VIDEO, bw_remaining_pct: 15, wred: true }
|
||||||
|
- { name: CRITICAL_DATA, bw_remaining_pct: 20, wred: true }
|
||||||
|
- { name: BUSINESS_DATA, bw_remaining_pct: 25, wred: true }
|
||||||
|
- { name: BULK_DATA, bw_remaining_pct: 10, wred: true }
|
||||||
|
- { name: SCAVENGER, bw_remaining_pct: 5, wred: true }
|
||||||
|
- { name: class-default, bw_remaining_pct: 20, fair_queue: true }
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ACL / PREFIX / COMMUNITY
|
||||||
|
###############################################################################
|
||||||
|
acls:
|
||||||
|
standard:
|
||||||
|
- name: "{{ snmp_acl }}"
|
||||||
|
rules:
|
||||||
|
- { action: permit, src: "{{ mgmt_net1 }}" }
|
||||||
|
- { action: permit, src: "{{ mgmt_net2 }}" }
|
||||||
|
extended:
|
||||||
|
- name: "{{ qos_acl_bulk }}"
|
||||||
|
rules: "{{ qos_rules_bulk | default([]) }}"
|
||||||
|
prefix_lists:
|
||||||
|
- name: DEFAULT
|
||||||
|
rules:
|
||||||
|
- { seq: 5, action: permit, prefix: 0.0.0.0/0 }
|
||||||
|
community_lists:
|
||||||
|
- name: BLACKHOLE
|
||||||
|
type: expanded
|
||||||
|
rules:
|
||||||
|
- { action: permit, regex: "_666$" }
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# SNMP
|
||||||
|
###############################################################################
|
||||||
|
snmp:
|
||||||
|
location: "{{ snmp_location }}"
|
||||||
|
contact: "{{ snmp_contact }}"
|
||||||
|
groups:
|
||||||
|
- { name: NOC_RO, version: 3, sec: authPriv, read_view: VIEW-STD }
|
||||||
|
users:
|
||||||
|
- { name: "{{ snmp_user }}", group: NOC_RO,
|
||||||
|
auth: sha, auth_pw: "{{ vault.snmp_auth }}",
|
||||||
|
priv: aes128, priv_pw: "{{ vault.snmp_priv }}" }
|
||||||
|
hosts:
|
||||||
|
- { ip: "{{ snmp_trap_host }}", vrf: "{{ mgmt_vrf }}",
|
||||||
|
version: 3, user: "{{ snmp_user }}" }
|
||||||
|
traps:
|
||||||
|
enable: "{{ snmp_traps | default(['snmp','bgp','ospf','link']) }}"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# INTERFACES (underlay + mgmt)
|
||||||
|
###############################################################################
|
||||||
|
interfaces:
|
||||||
|
mgmt:
|
||||||
|
- name: "{{ mgmt_intf }}"
|
||||||
|
vrf: "{{ mgmt_vrf }}"
|
||||||
|
ip: "{{ mgmt_ip }}"
|
||||||
|
mask: "{{ mgmt_mask }}"
|
||||||
|
uplinks:
|
||||||
|
- name: "{{ uplink1_intf }}"
|
||||||
|
desc: "To Core-1"
|
||||||
|
ip: "{{ uplink1_ip }}"
|
||||||
|
mask: "{{ uplink1_mask }}"
|
||||||
|
ospf: { area: "{{ ospf_area_uplink }}", cost: 10 }
|
||||||
|
qos_out: "{{ qos_policy_out }}"
|
||||||
|
- name: "{{ uplink2_intf }}"
|
||||||
|
desc: "To Core-2"
|
||||||
|
ip: "{{ uplink2_ip }}"
|
||||||
|
mask: "{{ uplink2_mask }}"
|
||||||
|
ospf: { area: "{{ ospf_area_uplink }}", cost: 10 }
|
||||||
|
qos_out: "{{ qos_policy_out }}"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# SLA
|
||||||
|
###############################################################################
|
||||||
|
sla:
|
||||||
|
probes:
|
||||||
|
- { type: icmp-echo, target: "{{ anycast_gw }}",
|
||||||
|
src: "{{ tunnel_intf }}", freq: 180, owner: DMVPN }
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# CONTROL-PLANE POLICING / MISC
|
||||||
|
###############################################################################
|
||||||
|
control_plane:
|
||||||
|
policy_in: "{{ cpp_policy | default('drop-udp') }}"
|
||||||
|
|
||||||
|
line:
|
||||||
|
con0:
|
||||||
|
exec_timeout: "{{ con_timeout | default('10 0') }}"
|
||||||
|
vty:
|
||||||
|
exec_timeout: "{{ vty_timeout | default('10 0') }}"
|
||||||
|
transport: "{{ vty_transport | default('ssh') }}"
|
||||||
|
acl_in: "{{ vty_acl | default('SECURE_ACCESS') }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Save this file as `data/<site>.yml`, merge it with the vault, and render.
|
||||||
|
The Jinja2 fragments in `config_components/` need only reference the keys above—no further CLI details leak into the YAML.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Below is a vendor-agnostic, scalable-template review written in “meta-config” form.
|
||||||
|
It is intentionally abstract (no literal values, no vendor CLI) so it can be mechanically translated to any NOS or rendered by an automation pipeline.
|
||||||
|
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
1. Meta-Template Structure
|
1. Meta-Template Structure
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user