Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/caveats.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ You can change the license file parameters within a node definition or with **de

* *netlab* supports only vJunosEvolved releases that do not require external PFE- and RPIO links. The first vJunosEvolved release implementing internal PFE- and RPIO links is the release 23.2R1-S1.8.
* vJunosEvolved software is supposed to be run on an Intel CPU. _netlab_ implements the hacks [suggested by Juniper to run vJunos on an AMD CPU](https://www.juniper.net/documentation/us/en/software/nce/nce-510-virtual-switches-mist-cloud-managed/amd-cpu_unofficial_tweaks.html), but please note that this is not supported.
* _netlab_ can start virtual machines running vJunosEvolved release 23.2 (release 24.1 and later requires UEFI BIOS). Use the vrnetlab-generated container as a workaround if you want to run vJunosEvolved release 24.1 or later.
* _netlab_ can start virtual machines running vJunosEvolved release 24.1+ (which requires UEFI BIOS - previous releases do not work with UEFI BIOS). Use the vrnetlab-generated container as a workaround if you want to run older vJunosEvolved releases.
* _netlab_ supports the [vrnetlab-generated](https://containerlab.dev/manual/kinds/vr-vjunosevolved/) vJunosEvolved container, not the cJunosEvolved container. The differences between the two make it impossible to run both container versions as the same virtual device, and cJunosEvolved offers no significant advantages at the moment.
* The vJunosEvolved container runs on Intel and AMD CPU.
* The virtual MAC address of the anycast gateway is ignored. _netlab_, therefore, does not support the anycast gateway on vPTX.
Expand Down
8 changes: 6 additions & 2 deletions docs/module/routing_protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ nodes:
(routing_af)=
## Address Families

Configuration modules for all IGP routing protocols that support multiple address families (IS-IS, EIGRP) or multiple protocol instances (OSPFv2, OSPFv3) support **_protocol_.af** global- or node-level module parameter. The **af** parameter can be a list- or a dictionary of address families.
Configuration modules for all IGP routing protocols that support multiple address families (IS-IS, EIGRP) or multiple protocol instances (OSPFv2, OSPFv3) support **_protocol_.af** global-, node-, or VRF-level module parameter. The **af** parameter can be a list or a dictionary of address families.

The default value of the **af** parameter is set based on address families configured on loopback- or physical interfaces -- an address family is enabled within an IGP configuration on a device if at least one interface on that device has an IP address from that address family.

```{warning}
If you decide to set a **‌_protocol_.af** parameter, you MUST specify which address families should be active. Using the **af** parameter just to set an unwanted address family to *False* without enabling any other address family will result in no address families being used.
```

**Example:**

```
Expand All @@ -61,7 +65,7 @@ nodes:
r5:
```

* IS-IS address families are enabled based on configured **isis.af** parameters
* IS-IS address families are enabled based on the configured **isis.af** parameters
* **ospf.af** parameter is not set -- OSPFv2 or OSPFv3 are enabled based on address families used on individual nodes

The following IS-IS address families are configured on individual routers:
Expand Down
2 changes: 2 additions & 0 deletions docs/module/vrf.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ You can also set these parameters to influence routing protocols within a VRF.
* **ospf.area** -- the default OSPF area for the VRF OSPF process (default: node **ospf.area**). It is configured on the VRF loopback interfaces.
* **bgp.router_id** -- per-VRF BGP router ID. You have to set this parameter if you want to configure inter-VRF EBGP sessions between interfaces of the same device.[^ELB]
* **ospf.router_id** -- per-VRF OSPF router ID. You can use this parameter for the same reasons as **bgp.router_id** or if you want consistent OSPF router IDs on Cisco IOS.
* **_igp_.af** -- specify the [address families](routing_af) you want to use with **isis**, **ospf**, or **rip** VRF instance.
* You can also specify OSPF route redistribution (**import**), **default** route origination, and default OSPF **password** and **timers**.

[^ELB]: That's how some people implement inter-VRF route leaking. You don't want to know the details ;)

Expand Down
2 changes: 1 addition & 1 deletion netsim/defaults/addressing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ p2p:
mgmt:
ipv4: 192.168.121.0/24
start: 100
mac: 08-4F-A9-00-00-00
mac: CA-FE-00-00-00-00
l2only:
vrf_loopback:
ipv4: 10.2.0.0/24
Expand Down
2 changes: 1 addition & 1 deletion netsim/devices/cat8000v.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ parent: csr
virtualbox:
image:
clab:
image: vrnetlab/vr-c8000v:17.13.01a
image: vrnetlab/cisco_c8000v:17.13.01a
node:
kind: cisco_c8000v
interface.name: eth{ifindex-1}
Expand Down
2 changes: 1 addition & 1 deletion netsim/devices/iosvl2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ clab:
ansible_ssh_pass: admin
ansible_user: admin
netlab_check_retries: 50
image: vrnetlab/cisco_viosl2:15.2.2020
image: vrnetlab/cisco_viosl2:15.2
node:
kind: linux
interface.name: eth{ifindex}
Expand Down
4 changes: 3 additions & 1 deletion netsim/devices/srlinux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ clab:
image: ghcr.io/nokia/srlinux:25.3.3
node:
kind: srl
type: ixrd2
type: ixr-d2
config_templates:
hosts: /etc/hosts
interface:
name: e1-{ifindex}
group_vars:
Expand Down
2 changes: 1 addition & 1 deletion netsim/devices/vptx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ libvirt:
create_template: vptx.xml.j2

clab:
image: vrnetlab/juniper_vjunosevolved:23.4R2-S2.1
image: vrnetlab/juniper_vjunosevolved:25.2R1.8-EVO
build: https://containerlab.dev/manual/kinds/vr-vjunosevolved/
mtu: 1500
node:
Expand Down
2 changes: 2 additions & 0 deletions netsim/install/libvirt/vptx.xml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
<ns0:arg value="type=0,vendor=Bochs,version=Bochs"/>
<ns0:arg value="-smbios"/>
<ns0:arg value="type=3,manufacturer=Bochs"/>
<ns0:arg value="-bios"/>
<ns0:arg value="/usr/share/qemu/OVMF.fd"/>
<ns0:arg value="-smbios"/>
<ns0:arg value="type=1,manufacturer=Bochs,product=Bochs,serial=chassis_no=0:slot=0:type=1:assembly_id=0x0D20:platform=251:master=0:channelized=no"/>
</ns0:commandline>
Expand Down
71 changes: 36 additions & 35 deletions netsim/modules/_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,38 @@
# * If the address families are not set, calculate them based on interface address families
# * Otherwise parse and validate the AF attribute
#
def routing_af(node: Box, proto: str, features: typing.Optional[Box] = None) -> None:
if 'af' in node[proto]: # Is the AF attribute set for the routing protocol?
if isinstance(node[proto].af,list): # Turn a list of address families into a dictionary
node[proto].af = { af: True for af in node[proto].af }

if node[proto].af is None:
node[proto].pop('af',None)
else:
if not isinstance(node[proto].af,dict):
log.error(
f'af attribute for {proto} on node {node.name} has to be a list or a dictionary',
log.IncorrectValue,
proto)
return

for proto_af in node[proto].af.keys():
if not proto_af in ('ipv4','ipv6'):
log.error(
f'Routing protocol address family has to be ipv4 and/or ipv6: {proto} on {node.name}',
log.IncorrectValue,
proto)
# Please note that this function could be called with node data (for global routing instance)
# or VRF data (acting as node data) for VRF routing instances
#
def routing_af(
node: Box, # Could also be VRF data
proto: str, # IGP we're checking
n_name: typing.Optional[str] = None, # Name of the object (default: node name)
is_vrf: bool = False, # Are we dealing with a VRF?
features: typing.Optional[Box] = None) -> None: # And finally, the device features

if n_name is None:
n_name = 'node {node.name}'
if 'af' in node[proto] and node[proto].af is None:
node[proto].pop('af',None)

if not 'af' in node[proto]: # No configured AF attribute, calculate it
for af in ['ipv4','ipv6']:
if af in node.get('loopback',{}): # Address family enabled on loopback?
node[proto].af[af] = True # ... we need it in the routing protocol
continue
for af in ['ipv4','ipv6']: # Process the loopback interface
lb_data = node.get('loopback',{}) # Fetch the loopback interface info
if isinstance(lb_data,Box): # ... watch out for bool values
if af in lb_data: # Address family enabled on loopback?
node[proto].af[af] = True # ... we need it in the routing protocol
continue

# Scan all interfaces -- they could be in node.interfaces or vrf_data.proto.interfaces
if_list = node[proto].get('interfaces',[]) if is_vrf else node.get('interfaces',[])
for l in if_list:
if 'vrf' in l and not is_vrf: # Skip VRF interfaces when processing global routing instance
continue # VRF instances will only have correct VRF interfaces anyway

for l in node.get('interfaces',[]): # Scan all interfaces
if proto in l and (af in l or af in l.get('dhcp.client',{})) \
and not 'vrf' in l: # Do we have AF enabled on any global interface?
node[proto].af[af] = True # Found it - we need it the module
# Do we have AF enabled on the global- or VRF interface
if proto in l and (af in l or af in l.get('dhcp.client',{})):
node[proto].af[af] = True # Found the AF - we need it in the routing protocol
continue

p_features = features.get(proto,{}) if features else {}
Expand All @@ -68,7 +68,7 @@ def routing_af(node: Box, proto: str, features: typing.Optional[Box] = None) ->

if af in p_features and p_features[af] is False:
log.error(
f'Device {node.device} (node {node.name}) cannot run {proto} on {af}',
f'{n_name} cannot run {proto} on {af}',
log.IncorrectValue,
proto)

Expand Down Expand Up @@ -297,6 +297,7 @@ def build_vrf_interface_list(
vrf_unnumbered_check: bool = True) -> None: # Check the source IP VRF for IPv4 unnumbereds

unnum_err_list = data.get_empty_box()
features = devices.get_device_features(node,topology.defaults)

for l in node.interfaces:
if proto not in l: # Not running the protocol on the interface?
Expand Down Expand Up @@ -346,7 +347,9 @@ def build_vrf_interface_list(
f"VRF {vname} on {node.name} has unnumbered interface(s) running {proto} without a VRF loopback",
category=log.MissingDependency,
module="vrf",
more_data=f"Used on interface(s) {','.join(unnum_err_list[vname])}")
more_data=f"Used on interface(s) {','.join(unnum_err_list[vname])}")

routing_af(vdata,proto,f'VRF {vname} on {node.name}',is_vrf=True,features=features)

#
# remove_unaddressed_intf -- remove all interfaces without IPv4 or IPv6 address from IGP
Expand Down Expand Up @@ -431,6 +434,7 @@ def check_vrf_protocol_support(
if not proto in v_data: # Are we running the target protocol in the VRF?
continue
if af and not af in v_data.af: # Does the VRF use the target address family?
v_data[proto].af.pop(af,None) # Make sure the AF is not set in VRF routing protocol data
continue

# We found the protocol to check in the current VRF, time to check device features
Expand All @@ -443,9 +447,6 @@ def check_vrf_protocol_support(
module=proto)
return

if af:
v_data[proto].af[af] = True # Add the AF to VRF routing protocol data

"""
check_intf_support -- check device support for optional interface parameters
"""
Expand Down Expand Up @@ -821,7 +822,7 @@ def igp_post_transform(
else:
remove_vrf_interfaces(node,proto)

routing_af(node,proto,features)
routing_af(node,proto,f'node {node.name}',features=features)
if vrf_aware:
remove_vrf_routing_blocks(node,proto)

Expand Down
3 changes: 3 additions & 0 deletions netsim/modules/isis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ attributes:
instance:
net: net
type:
vrf:
active: bool
af: { copy: global }
link:
metric: { type: int, min_value: 1, max_value: 16777215 }
cost: { type: int, min_value: 1, max_value: 16777215 }
Expand Down
1 change: 1 addition & 0 deletions netsim/modules/ospf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ attributes:
vrf_copy: [ area, router_id, reference_bandwidth ]
vrf:
active: bool
af: { copy: global }
area: { copy: global }
import: _r_import
default: { copy: node }
Expand Down
1 change: 1 addition & 0 deletions netsim/modules/ripv2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ attributes:
timers:
copy: global
vrf:
af: { copy: global }
active: bool
timers:
copy: global
Expand Down
2 changes: 1 addition & 1 deletion netsim/modules/srv6.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def node_pre_transform(self, node: Box, topology: Box) -> None:
node.srv6.locator = locator
locator_net = ipaddress.IPv6Network(locator)
if node.get('srv6.allocate_loopback'): # Auto-assign a loopback from locator range
first_host = next(locator_net.hosts()) # Use first usable address
first_host = next(iter(locator_net.hosts())) # Use first usable address
prefix6 = topology.pools['loopback'].get('prefix6',64) # Use loopback.prefix6, default /64
node.loopback.ipv6 = ipaddress.IPv6Interface((first_host, prefix6)).with_prefixlen

Expand Down
1 change: 1 addition & 0 deletions netsim/templates/provider/clab/srlinux/hosts-common.j2
15 changes: 15 additions & 0 deletions netsim/templates/provider/clab/srlinux/hosts.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#
# Created by netlab (containerlab Linux host)
#
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
#
{% if _hosts_entries is defined %}
{{ _hosts_entries }}
{% else %}
{% include 'hosts-common.j2' %}
{% endif %}
20 changes: 9 additions & 11 deletions netsim/templates/provider/libvirt/vptx-domain.j2
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
domain.driver = "kvm"
domain.nic_model_type = "virtio"
domain.graphics_type = "none"
domain.video_type = "none"
domain.nested = true
domain.cpu_mode = "custom"
domain.cpu_model = "qemu64"
{% if 'amd' in defaults.processor %}
domain.cpu_feature :name => 'svm', :policy => 'require'
{% else %}
domain.cpu_feature :name => 'vmx', :policy => 'require'
{% endif %}

{# -- add if condition here for UEFI (rel >24) #}
domain.loader = "/usr/share/OVMF/OVMF_CODE_4M.fd"
domain.nvram = "/var/lib/libvirt/qemu/nvram/{{ name }}.fd"

domain.machine_type = "q35"
domain.cpu_mode = "host-passthrough"

domain.sysinfo = {
"bios": {
"vendor": "Bochs",
Expand All @@ -34,8 +36,4 @@
"manufacturer": "Bochs"
}
}

{% if "amd" in defaults.processor|lower %}
domain.cpu_mode = "custom"
{% endif %}
end
2 changes: 1 addition & 1 deletion tests/errors/dup-mgmt-addr.log
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ MissingValue in nodes: Node r6 does not have a usable management IP addresss
IncorrectValue in nodes: Duplicate management ipv4 address 192.168.121.101 on r2 and r1
IncorrectValue in nodes: Duplicate management ipv4 address 192.168.121.105 on r7 and r5
IncorrectValue in nodes: Duplicate management ipv6 address 2001:db8:cafe::1 on r4 and r3
IncorrectValue in nodes: Duplicate management mac address 08:4f:a9:02:00:00 on r3 and r2
IncorrectValue in nodes: Duplicate management mac address ca:fe:00:02:00:00 on r3 and r2
Fatal error in netlab: Cannot proceed beyond this point due to errors, exiting
4 changes: 2 additions & 2 deletions tests/errors/dup-mgmt-addr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ ospf.timers.hello: 1
nodes:
r1:
mgmt:
mac: 0A:4F:A9:01:00:00
mac: CA:FE:00:01:00:00

r2:
mgmt:
ipv4: 192.168.121.101

r3:
mgmt:
mac: 08:4F:A9:02:00:00
mac: CA:FE:00:02:00:00
ipv6: 2001:db8:cafe::1

r4:
Expand Down
10 changes: 5 additions & 5 deletions tests/topology/expected/6pe.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ nodes:
mgmt:
ifname: GigabitEthernet0/0
ipv4: 192.168.121.104
mac: 08:4f:a9:04:00:00
mac: ca:fe:00:04:00:00
module:
- bgp
name: ce1
Expand Down Expand Up @@ -248,7 +248,7 @@ nodes:
mgmt:
ifname: GigabitEthernet0/0
ipv4: 192.168.121.105
mac: 08:4f:a9:05:00:00
mac: ca:fe:00:05:00:00
module:
- bgp
name: ce2
Expand Down Expand Up @@ -317,7 +317,7 @@ nodes:
mgmt:
ifname: GigabitEthernet0/0
ipv4: 192.168.121.103
mac: 08:4f:a9:03:00:00
mac: ca:fe:00:03:00:00
module:
- isis
- mpls
Expand Down Expand Up @@ -429,7 +429,7 @@ nodes:
mgmt:
ifname: GigabitEthernet0/0
ipv4: 192.168.121.101
mac: 08:4f:a9:01:00:00
mac: ca:fe:00:01:00:00
module:
- isis
- bgp
Expand Down Expand Up @@ -545,7 +545,7 @@ nodes:
mgmt:
ifname: GigabitEthernet0/0
ipv4: 192.168.121.102
mac: 08:4f:a9:02:00:00
mac: ca:fe:00:02:00:00
module:
- isis
- bgp
Expand Down
Loading