From 334f032926bbfb3d0f32428e6777a4c3a682eb65 Mon Sep 17 00:00:00 2001 From: Dan Ankers Date: Fri, 8 Sep 2023 11:54:06 +0000 Subject: [PATCH] Initial templates --- juniper-macros.j2 | 288 ++++++++++++++++++++++++++++++++++++++++++++++ juniper-vxlan.j2 | 53 +++++++++ 2 files changed, 341 insertions(+) create mode 100644 juniper-macros.j2 create mode 100644 juniper-vxlan.j2 diff --git a/juniper-macros.j2 b/juniper-macros.j2 new file mode 100644 index 0000000..40e79be --- /dev/null +++ b/juniper-macros.j2 @@ -0,0 +1,288 @@ +{% macro systemsection(device) %} +system { + host-name {{ device['name'] }}; + root-authentication { + encrypted-password "{{ device['config_context']['root_pw'] }}"; ## Client Higher + } + login { + class sysadmin { + permissions [ admin clear configure control firewall-control interface interface-control network reset rollback routing routing-control snmp snmp-control trace-control view view-configuration ]; + } +{% for user, details in device['config_context']['users'].items() %} + user {{ user }} { + uid 200{{ loop.index }}; + class {{ details['role'] }}; + authentication { + encrypted-password "{{ details['password'] }}"; ## SECRET-DATA + } + } +{% endfor %} + } + services { + ssh; + } + syslog { +{% for server in device['config_context']['syslog_servers'] %} + host {{ server }} { + any notice; + authorization info; + } +{% endfor %} + file interactive-commands { + interactive-commands any; + } + file messages { + any notice; + authorization info; + } + } +{% if device['config_context']['ntp_servers']|length > 0 %} + ntp { +{% for server in device['config_context']['ntp_servers'] %} + server {{ server }}; +{% endfor %} + } +{% endif %} +} +{%- endmacro %} + + +{% macro chassissection(device) %} +chassis { + aggregated-devices { + ethernet { + device-count {{ device['interfaces']|selectattr('type','eq','LAG')|list|count }}; {# Change this to be dynamically generated +#} + } + } +{% if device['config_context']['breakout_ports'] %} +{% for fpc, fpcdata in device['config_context']['breakout_ports'].items() %} + fpc {{ fpc }} { +{% for pic, picdata in fpcdata.items() %} + pic {{ pic }} { +{% for port, speed in picdata.items() %} + port {{ port }} { + channel-speed {{ speed }}; + } +{% endfor %} + } +{% endfor %} + } +{% endfor %} +{% endif %} +} +{%- endmacro %} + + +{% macro interfaceconfig(interface,rack = None) %} + {{ interface['name'] }} { +{% if interface.get('description') != '' %} + description "{{ interface['description'] }}"; +{% endif %} +{% if interface.get('mtu') and not interface.get('lag') %} + mtu {{ interface['mtu'] }}; +{% endif %} +{% if interface.get('lag') %} + ether-options { + 802.3ad {{ interface['lag']['name'] }}; + } +{% endif %} +{% if interface['name'].startswith('ae') %} +{% if interface['_custom_field_data']['esi_lag'] %} + esi { + auto-derive { + lacp; + } + all-active; + } +{% endif %} + aggregated-ether-options { + lacp { + periodic fast; +{% if interface['_custom_field_data']['system_id'] %} + system-id {{ interface['_custom_field_data']['system_id'] }}; +{% elif interface['_custom_field_data']['esi_lag'] %} + system-id 00:00:{{ '%02d' % rack|int }}:{% if interface['name'][2:]|int < 99 %}{{ '%02d' % interface['name'][2:]|int }}{% else %}{{ '%02x' % interface['name'][2:]|int }}{% endif %}:00:01; +{% endif %} + } + } +{% endif %} +{% if interface['ip_addresses']|length > 0 %} + unit 0 { + family inet { + address {{ interface['ip_addresses'][0]['address'] }}; + } + } +{% endif %} +{% if interface.get('mode') is not none %} + unit 0 { + family ethernet-switching { +{% if interface['mode']=='ACCESS' %} + interface-mode access; + vlan { + members {{ interface['untagged_vlan']['vid'] }}; + } +{% elif interface['mode']=='TAGGED' %} + interface-mode trunk; + vlan { + members [ {% for vlan in interface['tagged_vlans'] %}{{ vlan['vid'] }} {% endfor %}]; + } +{% endif %} + storm-control default; + } + } +{% endif %} + } +{%- endmacro %} + +{% macro interfacesection(device,vlans) %} +interfaces { +{% for interface in device['interfaces'] if interface['name'].startswith('xe-') or interface['name'].startswith('et-') %} +{{ interfaceconfig(interface) }} +{% endfor %} +{% for interface in device['interfaces'] if interface['name'].startswith('ae') %} +{{ interfaceconfig(interface,device['rack']['name'][5:]) }} +{% endfor %} +{% for interface in device['interfaces'] if interface['name']=='em0' %} +{{ interfaceconfig(interface) }} +{% endfor %} +{% for interface in device['interfaces'] if interface['name'].startswith('irb') %} +{% if loop.first %} + irb { +{% endif %} + unit {{ interface['name'][4:] }} { + description "{{ vlans[interface['name'][4:]|int] }}"; + family inet { + address {{ interface['ip_addresses'][0]['address'] }}; + } + } +{% if loop.last %} + } +{% endif %} +{% endfor %} +{% for interface in device['interfaces'] if interface['name']=='lo0' %} +{{ interfaceconfig(interface) }} +{% endfor %} +} +{%- endmacro %} + + +{% macro routingoptionssection(device) %} +routing-options { + router-id {{ device['primary_ip4']['address'][:-3] }}; + autonomous-system {{ device['config_context']['overlay_as'] }}; + forwarding-table { + export PFE-ECMP; + chained-composite-next-hop { + ingress { + evpn; + } + } + } +} +{%- endmacro %} + + +{% macro bgprrmeshgroup(name,devices) %} + group OVERLAY_RR_MESH { + type internal; + family evpn { + signaling; + } + bfd-liveness-detection { + minimum-interval 350; + multiplier 3; + session-mode automatic; + } +{% for dst_switch in devices if dst_switch['device_role']['name'] == 'Spine' and dst_switch['name'] != name %} + neighbor {{ dst_switch['primary_ip4']['address'][:-3] }}; +{% endfor %} + } +{%- endmacro %} + +{% macro bgpoverlaygroup(device, devices, route_reflector, other_role) %} + group OVERLAY { + type internal; + local-address {{ device['primary_ip4']['address'][:-3] }}; + family evpn { + signaling; + } +{% if route_reflector %} + cluster {{ device['primary_ip4']['address'][:-3] }}; + multipath; +{% endif %} + bfd-liveness-detection { + minimum-interval 350; + multiplier 3; + session-mode automatic; + } +{% for dst_switch in devices if dst_switch['device_role']['name'] == other_role %} + neighbor {{ dst_switch['primary_ip4']['address'][:-3] }}; +{% endfor %} + } +{%- endmacro %} + + + +{% macro bgpunderlaygroup(this_switch, devices, underlay_ips, underlay_as, other_role) %} + group UNDERLAY { + type external; + hold-time 10; + family inet { + unicast; + } + export BGP_LOOPBACK0; + local-as {{ underlay_as[this_switch] }}; + multipath { + multiple-as; + } +{% for dst_switch in devices if dst_switch['device_role']['name'] == other_role %} + neighbor {{ underlay_ips[dst_switch['name']][this_switch] }} { + peer-as {{ underlay_as[dst_switch['name']] }}; + } +{% endfor %} + } +{%- endmacro %} + + + +{% macro bgpsection(device, devices, underlay_ips, underlay_as) %} +{% if device['device_role']['name'] in ['Spine','Lab-Spine'] %} +{% set role='Spine' %} +{% set other_role='Leaf' %} +{% else %} +{% set role='Leaf' %} +{% set other_role='Spine' %} +{% endif %} +{% if device['device_role']['name'] == 'Lab-Spine' %} +{% set name=device['name'][4:] %} +{% else %} +{% set name=device['name'] %} +{% endif %} + bgp { +{{ bgpoverlaygroup(device, devices, role=='Spine', other_role) }} +{% if role == 'Spine' %} +{{ bgprrmeshgroup(name, devices) }} +{% endif %} +{{ bgpunderlaygroup(name,devices, underlay_ips, underlay_as, other_role) }} + } +{%- endmacro %} + + +{% macro vlanssection(vlans,device) %} +vlans { +{% for vid,name in vlans.items() %} + vl{{ vid }} { + vlan-id {{ vid }}; +{% if name != '' %} + description "{{ name }}"; +{% endif %} +{% if device['interfaces']|selectattr('name', 'equalto', 'irb.'+vid|string)|list|count %} + l3-interface irb.{{vid}}; +{% endif %} + vxlan { + vni {{ vid }}; + } + } +{% endfor %} +} +{%- endmacro %} diff --git a/juniper-vxlan.j2 b/juniper-vxlan.j2 new file mode 100644 index 0000000..6eaa82c --- /dev/null +++ b/juniper-vxlan.j2 @@ -0,0 +1,53 @@ +{% import "juniper-macros.j2" as junos %} +{{ junos.systemsection(device) }} +{% if device['device_role']['name'] == 'Leaf' %} +{{ junos.chassissection(device) }} +{% endif %} +{{ junos.interfacesection(device,vlans) }} +{% if device['device_role']['name'] == 'Leaf' %} +forwarding-options { + storm-control-profiles default { + all; + } +} +{% endif %} +policy-options { + policy-statement BGP_LOOPBACK0 { + term TERM1 { + from { + protocol direct; + route-filter {{ device['primary_ip4']['address'] }} exact; + } + then accept; + } + } + policy-statement PFE-ECMP { + then { + load-balance per-packet; + } + } +} +{{ junos.routingoptionssection(device) }} +protocols { +{{ junos.bgpsection(device, devices, underlay_ips, underlay_as) }} +{% if device['device_role']['name'] == 'Leaf' %} + evpn { + encapsulation vxlan; + extended-vni-list all; + } +{% endif %} + lldp { + interface all; + } +} +{% if device['device_role']['name'] == 'Leaf' %} +switch-options { + vtep-source-interface lo0.0; + route-distinguisher {{ device['primary_ip4']['address'][:-3] }}:1; + vrf-target { + target:64512:1111; + auto; + } +} +{{ junos.vlanssection(vlans, device) }} +{% endif %}