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.
|
||||
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
|
||||
--------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user