Lab Topology Attribute Validation
netlab includes a comprehensive validation framework that checks attributes of all core topology elements and optional configuration modules. Valid attributes, their data types, and other constraints are defined in the attributes
dictionary within the topology-defaults.yml
file and individual module definitions.
Attribute Validation Basics
Various data model transformation routines check:
Global attributes defined in
attributes.global
Address pool attributes defined in
attributes.pool
Group attributes defined in
attributes.group
and augmented withattributes.node
Node attributes defined in
attributes.node
Link attributes combined from
attributes.link
andattributes.link_internal
Interface attributes combined from
attributes.interface
andattributes.link
but withoutattributes.link_no_propagate
VLAN attributes defined in
attributes.vlan
VRF attributes defined in
attributes.vrf
Prefix attributes defined in
attributes.prefix
Group, node, link, interface, VLAN, and VRF attributes are augmented with module attributes based on modules configured on individual groups, nodes (node and interface attributes), or the list of all modules used in a lab topology (link, VLAN, VRF attributes).
attributes dictionary has additional elements that define extra attributes and propagation of attributes between lab topology objects:
internal global attributes are valid but not checked
link_internal link attributes are valid but not checked
Link module attributes are copied to interface attributes unless the module is mentioned in the link_module_no_propagate list
Pool attributes are copied to prefixes unless they are mentioned in the pool_no_copy list
Node attributes mentioned in the node_copy list are copied into node’s interfaces
Global module attributes are copied into node attributes unless they’re listed in a module no_propagate list
Individual modules could use additional values in the attributes dictionary. For example, the VLAN module uses the vlan_no_propagate list to control which VLAN attributes are not copied into links or SVI interfaces.
Specifying Valid Attributes
Core- or module-specific attribute types that are validated (global, node, link, interface, pool, prefix, groups, vlan, vrf) are specified as dictionaries of valid attributes.
The keys of the data type definition dictionaries are the valid attribute names, the values are the attribute data type descriptions (see also Special Attribute Dictionary Keys).
For example, this is the definition of valid gateway attributes and their data types:
gateway:
attributes:
global:
id: int
protocol: { type: str, valid_values: [ anycast, vrrp ] }
anycast:
unicast: bool
mac: mac
vrrp:
group: int
priority: int
preempt: bool
node:
protocol:
anycast:
vrrp:
link:
id: int
ipv4: { type: ipv4, use: interface }
protocol: { type: str, valid_values: [ anycast, vrrp ] }
anycast:
unicast: bool
mac: mac
vrrp:
group: int
priority: int
preempt: bool
The dictionary-based approach allows in-depth validation of nested attributes, where each attribute could be:
Another dictionary without the type key (use for nested attributes)
A string specifying the desired data type.
A dictionary specifying the data type in type key, and containing additional parameters described in Valid Data Types.
Valid Data Types
Validator recognizes standard Python data types (str, int, bool, list or dict) and the following networking-specific data types:
Data type. |
Meaning |
---|---|
asn |
AS number (an integer between 1 and 65535) |
device |
Valid device (platform) identifier |
id |
Identifier (containing A-Z, a-z, 0-9 and underscore) |
ipv4 |
An IPv4 address, prefix, integer (offset in a subnet), or bool (unnumbered) |
ipv6 |
An IPv6 address, prefix, integer (offset in a subnet), or bool (LLA only) |
mac |
MAC address in any format recognized by the |
net |
IS-IS NET/NSAP |
node_id |
Valid node name |
prefix_str |
An IPv4 or IPv6 prefix |
rd |
Route distinguisher (ASN:ID or IP:ID) |
The data type can be specified as a string (without additional parameters) or a dictionary with a type attribute (data type as a string) and other type-specific validation parameters.
For example, VLAN link parameters include access and native, which can be any string value, as well as mode that must have one of the predefined values:
vlan:
attributes:
link:
access: str
native: str
mode: { type: str, valid_values: [ bridge, irb, route] }
All attributes defined with a dictionary (mode in the above example, but not access or native) can use the following validation parameters:
true_value – value to use when the parameter is set to True
_requires – a list of modules that must be enabled in global- or node context to allow the use of this attribute. See
vrfs
inmodules/vrf.yml
andvlans
inmodules/vlan.yml
for more details._required (bool) – the attribute must be present if the parent in the parent dictionary
_alt_types – alternate data types
See Data Type Definition Examples for more details.
Further Data Type Validation Options
When an attribute has a data type defined with the type attribute, you can use the following attributes to perform value-based validations:
Data type |
Value validation option |
---|---|
str |
valid_values – list of valid values |
list |
valid_values – list of valid values, checked for every element in the list |
create_empty (bool) – replace None value with an empty list |
|
_subtype – validate values as belonging to the specified subtype |
|
dict |
create_empty (bool) – replace None value with an empty dictionary |
_keys – validation rules for individual dictionary keys. |
|
_subtype – validate values as belonging to the specified subtype |
|
_keytype – validate keys as belonging to the specified scalar type |
|
_list_to_dict – value can be specified as a list |
|
id |
max_length – maximum identifier length |
int |
min_value – minimum parameter value |
max_value – maximum parameter value |
|
ipv4 |
|
ipv6 |
Notes
_keys attribute is rarely used in dictionary definitions. Using a shortcut definition is much better. See examples for a counterexample.
You can also specify additional hints and help messages with an attribute:
Keyword |
Meaning |
---|---|
_help |
Help displayed when a value does not match the data type. |
_hint |
Additional hint(s) displayed together with an error message. |
Data Type Definition Examples
VLAN ID is an integer between 1 and 4K. VXLAN ID is an integer between 1 and 16M. This is how they are defined in the vlan definition:
attributes:
vlan:
id: { type: int, min_value: 1, max_value: 4095 }
vni: { type: int, min_value: 1, max_value: 16777215 }
BGP sessions is a dictionary of per-AF BGP session types. The value of bgp.attributes.global.sessions is defined as a dictionary which triggers recursive validation:
bgp.attributes:
global:
sessions:
ipv4: [ ibgp, ebgp ]
ipv6: [ ibgp, ebgp ]
Topology name definition contains an error message that should be more useful than the generic ‘identifier is…’ message:
global:
name:
type: id
_help: |
Topology name should be no longer than 16 characters. It should start with a letter
and contain letters, underscores or numbers. netlab derives it from directory name
when it's not specified in the lab topology file.
clab provider parameters include a type attribute, colliding with the validation type attribute. Valid clab attributes thus have to be specified with _keys dictionary:
clab.attributes:
node:
type: dict
_keys: # Make keys explicit to get around the 'type' attribute
binds:
kind: str
config_templates:
type: str
cmd: str
...
The global vlans dictionary can be used only with the vlan module:
attributes:
global:
vlans: # vlans is a valid global parameter
type: dict # It's a dictionary
_requires: [ vlan ] # ... that requires VLAN module
The global vrfs attribute is a dictionary of vrf definitions. The VRF names must be valid identifiers.
attributes:
global:
vrfs: # vrfs is a valid global parameter
type: dict # It's a dictionary
_subtype: vrf # ... of VRF definitions
_keytype: id # ... where the VRF names must be valid identifiers
_requires: [ vrf ] # ... that requires VRF module
You can specify a list of BGP session types for the MPLS 6PE functionality. However, you can also specify a True value for the global mpls.6pe attribute to enable the feature. The True value gets translated into a default list (enable 6PE on IBGP sessions):
mpls:
attributes:
global:
6pe: { type: list, true_value: [ ibgp ] }
Shortcut Data Type Definitions
netlab supports several shortcuts that make type definitions easier to create and read:
A data type definition that is a string is transformed into a dictionary with the type key.
A data type definition that is a list will match a list value. The value of the data type definition is used as the list of valid values.
A data type definition that is a dictionary without the type key will match a dictionary. Keys starting with ‘_’ (data validation parameters) will be retained in the data type definition, and all other keys will be transferred into the
_keys
dictionary.
String example
device_name: str
Is transformed into:
device_name:
type: str
List example:
x: [ a,b,c ]
… is transformed into
x:
type: list
valid_values: [ a,b,c ]
Dictionary example:
af:
_alt_types: [ NoneType ]
ipv4: bool
ipv6: bool
… is transformed into:
af:
_alt_types: [ NoneType ]
_keys:
ipv4: bool
ipv6: bool
User-Defined Data Types
Consider using user-defined data types if you use the same data structure in multiple places. You can define them in defaults.attributes and use them to validate any attribute.
Note
When using user-defined data types, you must specify them as a string value of a validated attribute. You cannot use user-defined data types as a value for type validation attribute.
For example, the bgp.session plugin defines BGP timers as exbs_timers
user-defined data type:
attributes: # User-defined data types
exbs_timers: # BGP timers: keepalive, hold, min_hold -- integers with a value range
keepalive:
type: int
min_value: 1
max_value: 300
hold:
type: int
min_value: 3
max_value: 3600
min_hold:
type: int
min_value: 3
max_value: 3600
The exbs_timers
data type is then used to validate bgp.timers global- and node attribute:
bgp:
attributes:
global:
timers: exbs_timers
...
node:
timers: exbs_timers
Please note that the following definition (using exbs_timers
as the value of timers
type attribute) would not work:
bgp:
attributes:
global:
timers:
type: exbs_timers ### INVALID, WON'T WORK
You can use the _namespace
attribute within the user-defined data types to add attributes from other objects. For example, as you can use link attributes in VLAN definitions, the vlan definition (see modules/vlan.yml
) includes the _namespace
attribute:
attributes:
vlan: # Define the VLAN object type
id: { type: int, min_value: 1, max_value: 4095 }
vni: { type: int, min_value: 1, max_value: 16777215 }
mode: { type: str, valid_values: [ bridge, irb, route ] }
prefix:
_namespace: [ link ] # VLANs can include link attributes
Alternate Data Types
Some netlab attributes could take a dictionary value, alternate values (True usually meaning use default, or None meaning I don’t care, do what you want), or a list of keys.
Specifying Alternate Data Types
_alt_types list (commonly used within a dictionary of valid attributes) specifies alternate data types that can be used instead of the primary data type. The types in the _alt_types list cannot be user-defined data types.
Example: igp.af attribute can take a dictionary of address families, or it could be left empty (None) to tell netlab to use the address families defined on the node:
ospf:
attributes:
global:
af:
_alt_types: [ NoneType ]
ipv4: bool
ipv6: bool
Example: A link prefix could be a dictionary (containing IPv4/IPv6 values checked elsewhere), a string that could be an IPv4 or IPv6 prefix, or a boolean value.
attributes:
link:
prefix:
type: dict
_alt_types: [ bool, prefix_str ]
Dictionary Specified As a List
Some netlab attributes that are supposed to be a dictionary can take a list value. That list value is transformed into a dictionary: list elements become dictionary keys, and a default value is used for the dictionary values. Add the _list_to_dict key to the attribute specification to validate such attributes. The _list_to_dict key specifies the default value used when converting a list into a dictionary.
_list_to_dict parameter is commonly used with address family parameters. For example, OSPF allows ospf.af parameter to be a list of enabled address families or a dictionary of address families with True/False values, for example:
nodes:
- name: c_nxos
device: nxos
ospf.af: [ ipv4 ]
- name: c_csr
device: csr
ospf.af.ipv4: True
The attribute definition for ospf.af attribute uses _list_to_dict to cope with both:
ospf:
attributes:
global:
af:
_list_to_dict: True
_alt_types: [ NoneType ]
ipv4: bool
ipv6: bool
IP Address Validation
ipv4 and ipv6 validators have a mandatory use parameter that can take the following values:
interface – an IP address specified on an interface. It can take a True/False value and does not have to include a prefix length.
prefix – an IP prefix. It can take a True/False value and must include prefix length/subnet mask.
id (IPv4 only) – an IPv4 address or an integer. Use id for parameters like OSPF areas, BGP cluster IDs, or router IDs.
Special Attribute Dictionary Keys
The attribute dictionaries can contain these special keys:
_namespace: a list of additional namespaces recognized within the object. For example, the vlan object can contain link attributes (see
netlab show attributes vlan --format yaml
for an example)_description: object description displayed in the netlab show attributes printout (see
netlab show attributes _v_entry --format yaml
for an example)