--- - hosts: all gather_facts: False # No need to reach out to the device for this play connection: local collections: - netbox.netbox module_defaults: group/netbox.netbox.netbox: netbox_url: "http://localhost:8000" netbox_token: "0123456789abcdef0123456789abcdef01234567" validate_certs: False tasks: # When using connection: local with a python-venv on # the ansible controller, we also need to use # delegate_to so that we use the python venv which # has all our deps installed in - name: Create fake manufacturer delegate_to: localhost run_once: True netbox_manufacturer: data: name: fake-manufacturer - name: Create router role delegate_to: localhost run_once: True netbox_device_role: data: name: router - name: Create our site delegate_to: localhost run_once: True netbox_site: data: name: fake site - name: Create a device_type for our spines delegate_to: localhost run_once: True netbox_device_type: data: slug: router model: fake-router-model manufacturer: fake-manufacturer - name: Create spine devices in netbox delegate_to: localhost netbox_device: data: name: "{{ inventory_hostname }}" device_type: router device_role: router site: fake-site - name: Create spine interfaces for each device in netbox delegate_to: localhost netbox_device_interface: data: device: "{{ inventory_hostname }}" name: "{{ item }}" type: 1000Base-t (1GE) # SR Linux uses the following format # https://containerlab.dev/manual/kinds/srl/#interfaces-mapping loop: - e1-1 - e1-2 - e1-3 - e1-4 - hosts: localhost tags: - ipam gather_facts: False collections: - netbox.netbox module_defaults: group/netbox.netbox.netbox: netbox_url: "http://localhost:8000" netbox_token: "0123456789abcdef0123456789abcdef01234567" validate_certs: False tasks: - name: Create cables for spine links tags: - cables netbox_cable: data: termination_a_type: dcim.interface termination_a: device: "{{ item.a_device }}" name: "{{ item.a_name }}" termination_b_type: dcim.interface termination_b: device: "{{ item.b_device }}" name: "{{ item.b_name }}" state: present loop: "{{ cables }}" - name: Create p2p prefix netbox_prefix: data: family: 6 prefix: "{{ p2p_prefix }}" site: fake-site - name: Set a fact for the total number of routers we have set_fact: router_count: "{{ groups['srl'] | length }}" - set_fact: conn_count: "{{ router_count | int * (router_count | int - 1)//2 }}" - name: Allocate /127s from p2p prefix netbox_prefix: data: family: 6 prefix: "{{ p2p_prefix | ansible.utils.ipsubnet(127, item) }}" site: fake-site # Create the number of prefixes equal to the number of connections in the mesh # via the formula N(N-1)/2 loop: "{{ range(0, conn_count | int) | list }}" - name: Assign IPs to each cable endpoint from each /127 netbox_ip_address: data: # Take the 0-based loop index, divide and round down, to get the # nth /127 subnet in the p2p prefix. We divide and round down because # we are looping through the cable array twice, once for the A side # and once for the B side of the connection, and we need both sides to be in # the same subnet prefix: "{{ p2p_prefix | ansible.utils.ipsubnet(127, (ansible_loop.index0 //2)) }}" # Request that NetBox find an available IP address in the prefix and # assign it to the interface assigned_object: # If the index0 of this loop is an even number (or the first), grab the # A side of the connection, otherwise grab the B side of the connection device: "{{ cables[ansible_loop.index0 // 2].b_device if ansible_loop.index0 % 2 else cables[ansible_loop.index0 // 2].a_device }}" name: "{{ cables[ansible_loop.index0 // 2].b_name if ansible_loop.index0 % 2 else cables[ansible_loop.index0 // 2].a_name }}" # Loop through the total number of connections we have, # but have two iterations for each connection loop: "{{ range(0, conn_count | int * 2 )| list }}" # Use the extended loop control feature so that ansible_loop.index0 is available loop_control: extended: true