Help us improve your experience.

Let us know what you think.

Do you have time for a two-minute survey?

 
 

Putting It All Together

The following use cases are examples of different network designs that utilize different methods and techniques of Apstra Freeform features discussed. These examples are meant to help you better understand how the features of Apstra and Freeform could be used to address various networking use cases.

Use Case #1 - Small Version of London Tube - CloudLabs Topology

This use case uses a small version of the London underground system as a map to design a network topology from arbitrary switches. The concept here is to create a very simple network design using Jinja templates and a property set. This use case demonstrates how you can create a complete network design using uncomplicated Jinja templating and property sets.

This use case is available as a hands-on lab using Juniper Apstra Cloudlabs. There is also a GitHub repository that includes the Jinja files and JSON property sets you can reference, contribute to, or even fork.

  1. Topology of the environment.

    The design in the blueprint

    All system links showing tags and IP addresses assigned via the topology editor/API.

  2. End state rendered configurations. For the Bond-Street device here is an example of the rendered configuration.

    interfaces Block policy-options Block protocols Block
     interfaces { 
        replace: xe-0/0/0 { 
          unit 0 { 
    interfaces { 
        replace: xe-0/0/0 { 
          unit 0 { 
            description "facing_oxford-circus:xe-0/0/1"; 
            family inet { 
              address 192.168.0.2/31; 
            } 
          } 
        } 
        replace: xe-0/0/1 { 
          unit 0 { 
            description "facing_green-park:xe-0/0/1"; 
            family inet { 
              address 192.168.0.8/31; 
            } 
          } 
        } 
        replace: lo0 { 
          unit 0 { 
              family inet { 
                  address 10.0.0.2/32; 
              } 
    protocols { 
      lldp { 
          port-id-subtype interface-name; 
          port-description-type interface-description; 
          neighbour-port-info-display port-id; 
          interface all; 
      } 
      replace: rstp { 
        bridge-priority 0; 
        bpdu-block-on-edge; 
      } 
      bgp { 
        group external-peers { 
          type external; 
          export send-direct; 
          neighbor 192.168.0.3 { 
            peer-as 22; 
            export add-med-110; 
          } 
          neighbor 192.168.0.9 { 
            peer-as 86; 
            export add-med-177; 
          } 
        } 
      } 
    } 
    routing-options { 
      autonomous-system  47; 
    } 
    protocols {
      lldp {
         port-id-subtype
    interface-name;
         port-description-type
    interface-description;
         neighbour-port-info-display port-id;
     interface all; } replace: rstp {
     bridge-priority 0;
     bpdu-block-on-edge; }
     bgp {
     group external-peers {
     type external;
     export send-direct;
     neighbor 192.168.0.3 {
     peer-as 22; export add-med-110;
     } neighbor 192.168.0.9 { peer-as 86; export add-med-177;
     } } } } 
    routing-options {
      autonomous-system 47;
    }
  3. Show Config Templates

    Config Template system.jinja Description
    system { 
        host-name {{hostname}}; 
    } 
    This template uses the builtin “hostname” variable from the device context to set the system host-name to this.
    Config Template interfaces.jinja Description
    {% set this_router=hostname %} 
    interfaces { 
    {% for interface_name, iface in interfaces.iteritems() %} 
        replace: {{ interface_name }} { 
          unit 0 { 
            description "{{iface['description']}}"; 
            family inet { 
              address {{iface['ipv4_address']}}/{{iface['ipv4_prefixlen']}}; 
            } 
          } 
        } 
    {% endfor %} 
        replace: lo0 {
          unit 0 { 
              family inet { 
                  address {{ property_sets.data[this_router]['loopback'] }}/32; 
              } 
          } 
       } 
    } 

    Set the variable this_router=hostname

    For loop that walks through the interfaces and inserts the proper interface stanza syntax for junos including the description of the interface and the family inet address and prefix length from the device context. You can populate these values via the links in topology editor.

    This inserts the property_set data for this router’s loopback address into the loopback stanza.

    Config Template protocols.jinja Description
    {% set this_router=hostname %} 
    policy-options { 
      policy-statement send-direct { 
          term 1 { 
              from protocol direct; 
              then accept; 
          } 
      } 
    {% for interface_name, iface in interfaces.iteritems() %} 
    {% set link_med = iface.link_tags[0] %} 
    {# 
      this may create multiple identical policy-statements, but JunOS is smart enough 
      to squash them. 
    #} 
      policy-statement add-med-{{ link_med }} { 
              from { 
                route-filter 0.0.0.0/0 longer; 
              } 
              then { 
                metric add {{ link_med }}; 
              } 
              then  { 
                accept 
              } 
      } 
    {% endfor %} 
    } 
     
    protocols { 
      lldp { 
          port-id-subtype interface-name; 
          port-description-type interface-description; 
          neighbour-port-info-display port-id; 
          interface all; 
      } 
      replace: rstp { 
        bridge-priority 0; 
        bpdu-block-on-edge; 
      } 
     
      bgp { 
        group external-peers { 
          type external; 
          export send-direct; 
          {% for interface_name, iface in interfaces.iteritems() %} 
          neighbor {{ iface.neighbor_interface.ipv4_address }} { 
            {% set peer_hostname=iface.neighbor_interface.system_hostname %} 
            peer-as {{ property_sets.data[peer_hostname]['asn'] }}; 
            export add-med-{{ iface.link_tags[0] }}; 
          } 
          {% endfor %} 
        } 
      } 
    } 
     
    routing-options { 
      autonomous-system  {{ property_sets.data[this_router]['asn'] }}; 
    } 

    Set the variable this_router = hostname from the device context.

    Set a policy-options stanza to send all directly attached routes.

    Walk the interface tree.

    Set the variable link_med to the tag set on the interface via the topology.

    Set the LLDP parameters

    Set the RSTP parameters

    Create the bgp stanza

    Group external-peers

    Type of external (ebgp)

    Export policy of send-direct

    Walk the interface tree and insert neighbors for any neighbor that has an ipv4 address. Set the peer_hostname variable to the neighbor interfaces hostname.

    Grab the peer as from the property_sets data “asn”

    Peer-as stanza is set with an export policy of add-med-tags

    Set the routing-options stanza with the autonomous system from the property sets asn value.

  4. Property Set

    Property Set "data" Description
    { 
        "bond-street": { 
          "asn": 47, 
          "loopback": "10.0.0.2" 
        }, 
        "green-park": { 
          "asn": 86, 
          "loopback": "10.0.0.4" 
        }, 
        "tottenham-court-road": { 
          "asn": 48, 
          "loopback": "10.0.0.3" 
        }, 
        "leicester-square": { 
          "asn": 137, 
          "loopback": "10.0.0.5" 
        }, 
        "piccadilly-circus": { 
          "asn": 23, 
          "loopback": "10.0.0.1" 
        }, 
        "oxford-circus": { 
          "asn": 22, 
          "loopback": "10.0.0.0" 
        } 
      } 

    The property set is straightforward represented as a list of dictionaries that include the following:

    • Name of the system or station is the key, and inside that key are two parameters

    • Asn which is the autonomous system number for BGP peering

    • Loopback which is the loopback address of the system/station to peer with.

Use Case #2 - Tags and Property Sets to Drive Day 2 Configuration

The small topology shown below has been constructed in the Freeform topology editor. It has three switches and two external systems named ESXi-1 & ESXi-2. The links facing the two hosts are tagged with esxBlueTrunk and esxRedTrunk respectively. The goal of this use case is to show how one way you can use the Freeform feature to dynamically build the switch trunk facing the ESXi hosts, determined by the entries in the Property Set and the tags assigned in the topology.

The role of the tag in this instance is to indicate where the trunk should be configured / created.

The role of the Property Set in this instance is to hold the relevant data required to configure the trunk members, the VLANs, and the IRBs.

This example describes the power of utilizing a carefully crafted Configuration Template (Jinja2), Tags and Property Sets to build configuration, without the need to re-craft the Config Template. As new tags are assigned to the topology, or new VNs are assigned to the property set, the associated config will be dynamically built for these trunks.

End-state configuration

For the interface tagged with esxBlueTrunk, the final Junos configuration will include:

Interface Block IRB Block VLAN Block
interfaces { 
  ae2 { 
  description esxBlueTrunk 
    unit 0 {             
      family ethernet-switching { 
          interface-mode trunk 
          vlan { 
            members [ 
                    vn99 
                    vn100 
                    vn101 
            ] 
         } 
      }           
    } 
  } 
irb { 
    unit 99 { 
        family inet { 
            mtu 9000; 
            address 1.1.99.1/24; 
        } 
    } 
    unit 100 { 
        family inet { 
            mtu 9000; 
            address 1.1.100.1/24; 
        } 
    } 
    unit 101 { 
        family inet { 
            mtu 9000; 
            address 1.1.101.1/24; 
        } 
    } 
  } 
} 
vlans { 
  vn99 { 
    vlan-id 99; 
    description vMotionVN-99; 
    l3-interface irb.99; 
  } 
  vn100 { 
    vlan-id 100; 
    description storageVN-100; 
    l3-interface irb.100; 
  } 
  vn101 { 
    vlan-id 101; 
    description mgmtVN-101; 
    l3-interface irb.101; 
  } 
} 

esxTrunk Property Set

The Property Set includes the necessary details needed to build the Junos Trunk config

esxTrunk Property Set Description

The esxTrunk Property Set (to the left) has been constructed as a dictionary of dictionaries for good reason: to enable recursive lookup. The esxBlueTrunk dictionary has been expanded to show values 99, 100, 101 which in this instance are used as both VLAN IDs, and as a key to the dictionary below it. The dictionaries below provide key : value pairs for the subnet, the gateway, and the description. In this example, the subnet is not required and exists purely for reference.

With the data organized in this way, the Config Template has been designed to recurse through two data structures to search for matching tags:

  1. The esxTrunk Property Set

  2. The topology from the topology editor

When the tag in both data sets match, the Config Template produces the desired configuration. By cross-referencing the Property Set to the left with the Junos configuration output above, you can see that:

  • The values esx[Red | Blue | Pink]Trunk, are used as the tags to match, as we walk through both datasets.

  • The values 99, 100, 101 are used as the VLAN IDs

  • The gateway is used as the IRB address

  • The description is used to overwrite the original interface description (as required)

Both the esx(Red / Pink)Trunk, hold similar information as the esxBlueTrunk

To enable efficient recursive walking of both the Property Set to the left and the tags assigned to the links in the topology, the tags have been purposely assigned the same values

  • esxRedTrunk

  • esxBlueTrunk

  • esxPinkTrunk

Jinja2 base Config Template State Machine

The Config Template esxTrunks.jinja flow is described below.

Jinja2 base Config Template

Config Template Description
{% set Rendered_VNs = {} %} 
{% for ps_tag in property_sets.esxTrunk %} 
 
 
 
  {% for interface_name, iface in interfaces.iteritems() %}  
    {% if ((iface.link_tags) and (ps_tag in iface.link_tags)) %} 
   
 
interfaces { 
  {{interface_name}} { 
  description {{ ps_tag }} 
     
 
  unit 0 {             
      family ethernet-switching { 
          interface-mode trunk 
          vlan { 
            members [ 
                {% for vlan_id in property_sets.esxTrunk[ps_tag] %} 
                {% set _ = Rendered_VNs.update({vlan_id: ps_tag}) %} 
                    vn{{ vlan_id }} 
                {% endfor %} 
            ] 
         } 
      }           
    } 
  } 
  {% endif %} 
  {% endfor %}   
  {% endfor %} 
   
irb { 
{% for vn in Rendered_VNs %} 
{% set tag = Rendered_VNs[vn] %} 
    unit {{ vn }} { 
        family inet { 
            mtu 9000; 
            address {{ property_sets.esxTrunk[tag][vn]['gateway'] }}; 
        } 
    } 
    {% endfor %} 
  } 
} 
vlans { 
{% for vn in Rendered_VNs|unique %} 
{% set tag = Rendered_VNs[vn] %} 
  vn{{ vn }} { 
    vlan-id {{ vn }}; 
    description {{ property_sets.esxTrunk[tag][vn]['description'] }}-{{ vn }}; 
    l3-interface irb.{{ vn }}; 
  } 
  {% endfor %} 
} 

A global dictionary to store VNs to render

Start by traversing the esxTrunk Property Set shown above. The first value retrieved, and stored in the variable ps_tag (Property Set tag) is one of the strings:

  • esxRedTrunk

  • esxBlueTrunk

  • esxPinkTrunk

Now traverse or ‘iterate’ through the interfaces in the topology

If an interface link_tag exists and the ps_tag is in the list of interface link tags, the condition has been met where trunk configuration will be rendered

Start outputting the interfaces block . Output the interface_name where ps_tag equals the interface link tag . Optionally assign a description, although Freeform will have already assigned this description from the topology

Set the Unit number and associated config to describe the trunk

Set the trunk member

Traverse the esxTrunk Property Set using the ps_tag to retrieve the VLAN IDs

Enter each VLAN ID in the dictionary declared at line 1for later use

Output the vn VLAN ID detailed in the esxTrunk Property Set

End the for loop when there are no more entries in the esxTrunk Property Set

End the if statement above

End the for statement above

End the for statement above

Start outputting the irb block

Traverse the Rendered_VNs dictionary

For readability, set the variable tag to the tag stored in the dictionary

Set the unit number using the vlan_id

Set the gateway address stored in the esxTrunk PropertySet using the tag, vn and the key ‘gateway’ to access the stored string

End the for statement above

Starer outputting the vlans block

As per the irb block above, traverse the Rendered_VNs dictionary,

For readability, set the variable tag to the tag stored in the dictionary

Set the vn number

Set the vlan_id ID

Set the description as required

Set the layer3 irb number

End the for statement above

The Jinja2 based Config Template shown below, utilizes both the assigned tag and the Property Set to build the required config.

Use Case #3 - Advanced Example Using CRB - CloudLabs Topology

This final use case is a complete CRB example that has been written by Apstra Engineering. This example is built completely with static Jinja templates and all of the data is in property sets for the network/devices. This allows the users of this CRB example to operate / expand / change the network just by editing property-sets and allowing you to not touch the underlying Jinja templates. The purpose of this use case is to give you an example of the art of the possible and to demonstrate the flexibility and power of the Freeform feature. All the configuration templates, property sets, and jinja templates and functions have embedded documentation to help you understand the use and function.

This use case is available to you as a fully deployed hands-on sandbox using Juniper Apstra Cloudlabs. There is also a GitHub repository that includes all the same files. you can use this as examples of how to perform certain functions to make your own advanced Config Templates for your use case https://www.juniper.net/us/en/forms/apstra-free-trial.html