Skip to content

PyAVD

PyAVD is a python package providing some of the features from the arista.avd Ansible collection without requiring Ansible. PyAVD leverages the same logic as the Ansible collection, so the generated outputs should be exactly the same based on the same inputs.

PyAVD does not provide any inventory or variable management, so PyAVD cannot replace a full Ansible based solution by itself. PyAVD could serve as an element in larger framework.

Supported features:

  • Validation of inputs based on the eos_designs input schema.
  • Generation of “avd_facts” and “structured config” to be used in other PyAVD functions.
  • Validation of “structured config” based on the eos_cli_config_gen input schema.
  • Generation of device configuration.
  • Generation of device documentation.

Feedback is very welcome. Please use GitHub discussions.

Functions overview

Arista AVD Overview Arista AVD Overview

Known limitations

Warning

Input data and “structured_configs” will be in-place updated by various PyAVD functions. Make sure to deep copy the data first if modifications are not allowed.

Warning

get_device_structured_config(), get_device_config() and get_device_doc() are not thread-safe, so avoid running them for the same device across multiple threads.

Note

  • No support for inline Jinja2 or custom Jinja2 templates.
  • The logic uses the hostname as the unique identifier for each device, so overlapping hostnames will not work.
  • For get_avd_facts(), fabric_name is not used or verified and may differ between devices. All devices in the given inputs will be treated as one fabric.
  • hostname must be set in “structured_config” for each device. hostname will be set correctly when using get_structured_config().

Roadmap

Note

Subject to change. No commitments implied.

  • Add examples
  • Add more tests (current coverage is 85%)
  • Add network state validation similar to eos_validate_state.
  • Add CloudVision tag integrations
  • Explore support for custom Jinja2 templates.

Installation

Install the pyavd Python package:

pip install pyavd

Python dependencies are automatically installed with above command.

Optional requirements

To install Ansible AVD collection Python requirements install with extra ansible:

pip install pyavd[ansible]

Reference

pyavd.validate_inputs.validate_inputs(inputs)

Validate input variables according to the eos_designs schema as documented on avd.arista.com.

Where supported by the schema, types will be auto type-converted like from “int” to “str”.

Parameters:

Name Type Description Default
inputs dict

Dictionary with inputs for “eos_designs”.

required

Returns:

Type Description
ValidationResult

Validation result object with any validation errors or deprecation warnings.

Source code in python-avd/pyavd/validate_inputs.py
def validate_inputs(inputs: dict) -> ValidationResult:
    """
    Validate input variables according to the `eos_designs` schema as documented on avd.arista.com.

    Where supported by the schema, types will be auto type-converted like from "int" to "str".

    Args:
        inputs: Dictionary with inputs for "eos_designs".

    Returns:
        Validation result object with any validation errors or deprecation warnings.
    """
    # pylint: disable=import-outside-toplevel
    from .avd_schema_tools import EosDesignsAvdSchemaTools

    # pylint: enable=import-outside-toplevel

    eos_designs_schema_tools = EosDesignsAvdSchemaTools()

    # Inplace conversion of data
    validation_result = eos_designs_schema_tools.convert_data(inputs)

    # Validate input data
    validation_result.merge(eos_designs_schema_tools.validate_data(inputs))
    return validation_result

pyavd.get_avd_facts.get_avd_facts(all_inputs)

Build avd_facts using the AVD eos_designs_facts logic.

Variables should be converted and validated according to AVD eos_designs schema first using pyavd.validate_inputs.

Note! No support for inline templating or jinja templates for descriptions or ip addressing

Parameters:

Name Type Description Default
all_inputs dict[str, dict]

A dictionary where keys are hostnames and values are dictionaries of input variables per device.

{
    "<hostname1>": dict,
    "<hostname2>": dict,
    ...
}

required

Returns:

Type Description
dict[str, dict]

Nested dictionary with various internal “facts”. The full dict must be given as argument to pyavd.get_device_structured_config:

{"avd_switch_facts": dict, "avd_overlay_peers": dict, "avd_topology_peers": dict}

Source code in python-avd/pyavd/get_avd_facts.py
def get_avd_facts(all_inputs: dict[str, dict]) -> dict[str, dict]:
    """
    Build avd_facts using the AVD eos_designs_facts logic.

    Variables should be converted and validated according to AVD `eos_designs` schema first using `pyavd.validate_inputs`.

    Note! No support for inline templating or jinja templates for descriptions or ip addressing

    Args:
        all_inputs: A dictionary where keys are hostnames and values are dictionaries of input variables per device.
            ```python
            {
                "<hostname1>": dict,
                "<hostname2>": dict,
                ...
            }
            ```

    Returns:
        Nested dictionary with various internal "facts". The full dict must be given as argument to `pyavd.get_device_structured_config`:
            ```python
            {"avd_switch_facts": dict, "avd_overlay_peers": dict, "avd_topology_peers": dict}
            ```
    """
    avd_switch_facts_instances = _create_avd_switch_facts_instances(all_inputs)
    avd_switch_facts = _render_avd_switch_facts(avd_switch_facts_instances)
    avd_overlay_peers, avd_topology_peers = _render_peer_facts(avd_switch_facts)

    return {
        "avd_switch_facts": avd_switch_facts,
        "avd_overlay_peers": avd_overlay_peers,
        "avd_topology_peers": avd_topology_peers,
    }

pyavd.get_device_structured_config.get_device_structured_config(hostname, inputs, avd_facts)

Build and return the AVD structured configuration for one device.

Parameters:

Name Type Description Default
hostname str

Hostname of device.

required
inputs dict

Dictionary with inputs for “eos_designs”. Variables should be converted and validated according to AVD eos_designs schema first using pyavd.validate_inputs.

required
avd_facts dict

Dictionary of avd_facts as returned from pyavd.get_avd_facts.

required

Returns:

Type Description
dict

Device Structured Configuration as a dictionary

Source code in python-avd/pyavd/get_device_structured_config.py
def get_device_structured_config(hostname: str, inputs: dict, avd_facts: dict) -> dict:
    """
    Build and return the AVD structured configuration for one device.

    Args:
        hostname: Hostname of device.
        inputs: Dictionary with inputs for "eos_designs".
            Variables should be converted and validated according to AVD `eos_designs` schema first using `pyavd.validate_inputs`.
        avd_facts: Dictionary of avd_facts as returned from `pyavd.get_avd_facts`.

    Returns:
        Device Structured Configuration as a dictionary
    """
    # pylint: disable=import-outside-toplevel
    from ._eos_designs.structured_config import get_structured_config
    from ._errors import AristaAvdError
    from .avd_schema_tools import AvdSchemaTools
    from .constants import EOS_CLI_CONFIG_GEN_SCHEMA_ID, EOS_DESIGNS_SCHEMA_ID

    # pylint: enable=import-outside-toplevel
    #
    # Set 'inventory_hostname' on the input hostvars, to keep compatibility with Ansible focused code.
    # Also map in avd_facts without touching the hostvars
    mapped_hostvars = ChainMap(
        {
            "inventory_hostname": hostname,
            "switch": avd_facts["avd_switch_facts"][hostname]["switch"],
        },
        avd_facts,
        inputs,
    )

    input_schema_tools = AvdSchemaTools(schema_id=EOS_DESIGNS_SCHEMA_ID)
    output_schema_tools = AvdSchemaTools(schema_id=EOS_CLI_CONFIG_GEN_SCHEMA_ID)
    result = {}

    # We do not validate input variables in this stage (done in "validate_inputs")
    structured_config = get_structured_config(
        vars=mapped_hostvars,
        input_schema_tools=input_schema_tools,
        output_schema_tools=output_schema_tools,
        result=result,
        templar=None,
        validate=False,
    )
    if result.get("failed"):
        msg = f"{[str(error) for error in result['errors']]}"
        raise AristaAvdError(msg)

    return structured_config

pyavd.validate_structured_config.validate_structured_config(structured_config)

Validate structured config according the eos_cli_config_gen schema as documented on avd.arista.com.

Where supported by the schema, types will be auto type-converted like from “int” to “str”.

Parameters:

Name Type Description Default
structured_config dict

Dictionary with structured configuration.

required

Returns:

Type Description
ValidationResult

Validation result object with any validation errors or deprecation warnings.

Source code in python-avd/pyavd/validate_structured_config.py
def validate_structured_config(structured_config: dict) -> ValidationResult:
    """
    Validate structured config according the `eos_cli_config_gen` schema as documented on avd.arista.com.

    Where supported by the schema, types will be auto type-converted like from "int" to "str".

    Args:
        structured_config: Dictionary with structured configuration.

    Returns:
        Validation result object with any validation errors or deprecation warnings.
    """
    # pylint: disable=import-outside-toplevel
    from .avd_schema_tools import EosCliConfigGenAvdSchemaTools

    # pylint: enable=import-outside-toplevel

    eos_cli_config_gen_schema_tools = EosCliConfigGenAvdSchemaTools()
    # Inplace conversion of data
    validation_result = eos_cli_config_gen_schema_tools.convert_data(structured_config)

    # Validate input data
    validation_result.merge(eos_cli_config_gen_schema_tools.validate_data(structured_config))
    return validation_result

pyavd.get_fabric_documentation.get_fabric_documentation(avd_facts, structured_configs, fabric_name, fabric_documentation=True, include_connected_endpoints=False, topology_csv=False, p2p_links_csv=False)

Build and return the AVD fabric documentation.

The returned object will contain the content of the requested documentation areas: - Fabric documentation as Markdown, optionally including connected endpoints. - Topology CSV containing the physical interface connections for every device. - P2P links CSV containing the Routed point-to-point links.

Parameters:

Name Type Description Default
avd_facts dict[str, dict]

Dictionary of avd_facts as returned from pyavd.get_avd_facts.

required
structured_configs dict[str, dict]

Dictionary of structured configurations for all devices, keyed by hostname.

required
fabric_name str

Name of the fabric. Only used for the main heading in the Markdown documentation.

required
fabric_documentation bool

Returns fabric documentation when set to True.

True
include_connected_endpoints bool

Includes connected endpoints in the fabric documentation when set to True.

False
topology_csv bool

Returns topology CSV when set to True.

False
p2p_links_csv bool

Returns P2P links CSV when set to True.

False

Returns:

Type Description
FabricDocumentation

FabricDocumentation object containing the requested documentation areas.

Source code in python-avd/pyavd/get_fabric_documentation.py
def get_fabric_documentation(
    avd_facts: dict[str, dict],
    structured_configs: dict[str, dict],
    fabric_name: str,
    fabric_documentation: bool = True,
    include_connected_endpoints: bool = False,
    topology_csv: bool = False,
    p2p_links_csv: bool = False,
) -> FabricDocumentation:
    """
    Build and return the AVD fabric documentation.

    The returned object will contain the content of the requested documentation areas:
    - Fabric documentation as Markdown, optionally including connected endpoints.
    - Topology CSV containing the physical interface connections for every device.
    - P2P links CSV containing the Routed point-to-point links.

    Args:
        avd_facts: Dictionary of avd_facts as returned from `pyavd.get_avd_facts`.
        structured_configs: Dictionary of structured configurations for all devices, keyed by hostname.
        fabric_name: Name of the fabric. Only used for the main heading in the Markdown documentation.
        fabric_documentation: Returns fabric documentation when set to True.
        include_connected_endpoints: Includes connected endpoints in the fabric documentation when set to True.
        topology_csv: Returns topology CSV when set to True.
        p2p_links_csv: Returns P2P links CSV when set to True.

    Returns:
        FabricDocumentation object containing the requested documentation areas.
    """
    # pylint: disable=import-outside-toplevel
    from pyavd._eos_designs.fabric_documentation_facts import FabricDocumentationFacts
    from pyavd.j2filters import add_md_toc

    from .constants import EOS_DESIGNS_JINJA2_PRECOMPILED_TEMPLATE_PATH
    from .templater import Templar
    # pylint: enable=import-outside-toplevel

    fabric_documentation_facts = FabricDocumentationFacts(avd_facts, structured_configs, fabric_name, include_connected_endpoints)
    result = FabricDocumentation()
    doc_templar = Templar(precompiled_templates_path=EOS_DESIGNS_JINJA2_PRECOMPILED_TEMPLATE_PATH)
    if fabric_documentation:
        fabric_documentation_facts_dict = fabric_documentation_facts.render()
        result.fabric_documentation = doc_templar.render_template_from_file("fabric_documentation.j2", fabric_documentation_facts_dict)
        if include_connected_endpoints:
            result.fabric_documentation += "\n" + doc_templar.render_template_from_file("connected_endpoints_documentation.j2", fabric_documentation_facts_dict)

        result.fabric_documentation = add_md_toc(result.fabric_documentation, skip_lines=3)

    if topology_csv:
        result.topology_csv = _get_topology_csv(fabric_documentation_facts)
    if p2p_links_csv:
        result.p2p_links_csv = _get_p2p_links_csv(fabric_documentation_facts)
    return result

pyavd.get_device_config.get_device_config(structured_config)

Render and return the device configuration using AVD eos_cli_config_gen templates.

Parameters:

Name Type Description Default
structured_config dict

Dictionary with structured configuration. Variables should be converted and validated according to AVD eos_cli_config_gen schema first using pyavd.validate_structured_config.

required

Returns:

Type Description
str

Device configuration in EOS CLI format.

Source code in python-avd/pyavd/get_device_config.py
def get_device_config(structured_config: dict) -> str:
    """
    Render and return the device configuration using AVD eos_cli_config_gen templates.

    Args:
        structured_config: Dictionary with structured configuration.
            Variables should be converted and validated according to AVD `eos_cli_config_gen` schema first using `pyavd.validate_structured_config`.

    Returns:
        Device configuration in EOS CLI format.
    """
    # pylint: disable=import-outside-toplevel
    from .constants import EOS_CLI_CONFIG_GEN_JINJA2_CONFIG_TEMPLATE, EOS_CLI_CONFIG_GEN_JINJA2_PRECOMPILED_TEMPLATE_PATH
    from .templater import Templar

    # pylint: enable=import-outside-toplevel

    templar = Templar(precompiled_templates_path=EOS_CLI_CONFIG_GEN_JINJA2_PRECOMPILED_TEMPLATE_PATH)
    return templar.render_template_from_file(EOS_CLI_CONFIG_GEN_JINJA2_CONFIG_TEMPLATE, structured_config)

pyavd.get_device_doc.get_device_doc(structured_config, add_md_toc=False)

Render and return the device documentation using AVD eos_cli_config_gen templates.

Parameters:

Name Type Description Default
structured_config dict

Dictionary with structured configuration. Variables should be converted and validated according to AVD eos_cli_config_gen schema first using pyavd.validate_structured_config.

required
add_md_toc bool

Add a table of contents for markdown headings.

False

Returns:

Type Description
str

Device documentation in Markdown format.

Source code in python-avd/pyavd/get_device_doc.py
def get_device_doc(structured_config: dict, add_md_toc: bool = False) -> str:
    """
    Render and return the device documentation using AVD eos_cli_config_gen templates.

    Args:
        structured_config: Dictionary with structured configuration.
            Variables should be converted and validated according to AVD `eos_cli_config_gen` schema first using `pyavd.validate_structured_config`.
        add_md_toc: Add a table of contents for markdown headings.

    Returns:
        Device documentation in Markdown format.
    """
    # pylint: disable=import-outside-toplevel
    from .constants import EOS_CLI_CONFIG_GEN_JINJA2_DOCUMENTAITON_TEMPLATE, EOS_CLI_CONFIG_GEN_JINJA2_PRECOMPILED_TEMPLATE_PATH
    from .j2filters import add_md_toc as filter_add_md_toc
    from .templater import Templar

    # pylint: enable=import-outside-toplevel

    templar = Templar(precompiled_templates_path=EOS_CLI_CONFIG_GEN_JINJA2_PRECOMPILED_TEMPLATE_PATH)
    result: str = templar.render_template_from_file(EOS_CLI_CONFIG_GEN_JINJA2_DOCUMENTAITON_TEMPLATE, structured_config)
    if add_md_toc:
        return filter_add_md_toc(result, skip_lines=3)

    return result

pyavd.validation_result.ValidationResult

Object containing result of data validation.

Attributes:

Name Type Description
failed bool

True if data is not valid according to the schema. Otherwise False.

validation_errors list[AvdValidationError]

List of AvdValidationErrors containing schema violations.

deprecation_warnings list[AvdDeprecationWarning]

List of AvdDeprecationWarnings containing warning for deprecated inputs.

Source code in python-avd/pyavd/validation_result.py
class ValidationResult:
    """
    Object containing result of data validation.

    Attributes:
        failed: True if data is not valid according to the schema. Otherwise False.
        validation_errors: List of AvdValidationErrors containing schema violations.
        deprecation_warnings: List of AvdDeprecationWarnings containing warning for deprecated inputs.
    """

    failed: bool
    validation_errors: list[AvdValidationError]
    deprecation_warnings: list[AvdDeprecationWarning]

    def __init__(self, failed: bool, validation_errors: list | None = None, deprecation_warnings: list | None = None) -> None:
        self.failed = failed
        self.validation_errors = validation_errors or []
        self.deprecation_warnings = deprecation_warnings or []

    def merge(self, other: ValidationResult) -> None:
        """Merge another ValidationResult instance into this instance."""
        if not isinstance(other, ValidationResult):
            msg = f"Unable to merge type '{type(other)}' into 'ValidationResult"
            raise TypeError(msg)

        self.failed = self.failed or other.failed
        self.validation_errors.extend(other.validation_errors)
        self.deprecation_warnings.extend(other.deprecation_warnings)

pyavd.validation_result.ValidationResult.merge(other)

Merge another ValidationResult instance into this instance.

Source code in python-avd/pyavd/validation_result.py
def merge(self, other: ValidationResult) -> None:
    """Merge another ValidationResult instance into this instance."""
    if not isinstance(other, ValidationResult):
        msg = f"Unable to merge type '{type(other)}' into 'ValidationResult"
        raise TypeError(msg)

    self.failed = self.failed or other.failed
    self.validation_errors.extend(other.validation_errors)
    self.deprecation_warnings.extend(other.deprecation_warnings)

pyavd.api.fabric_documentation.FabricDocumentation

Object containing the requested documentation.

Attributes:

Name Type Description
fabric_documentation str

Fabric Documentation as Markdown.

topology_csv str

Topology CSV containing the physical interface connections for every device.

p2p_links_csv str

P2P links CSV containing the Routed point-to-point links.

Source code in python-avd/pyavd/api/fabric_documentation/__init__.py
class FabricDocumentation:
    """
    Object containing the requested documentation.

    Attributes:
        fabric_documentation: Fabric Documentation as Markdown.
        topology_csv: Topology CSV containing the physical interface connections for every device.
        p2p_links_csv: P2P links CSV containing the Routed point-to-point links.
    """

    fabric_documentation: str = ""
    topology_csv: str = ""
    p2p_links_csv: str = ""

AVD eos_designs base module to generate interface descriptions.

pyavd.api.interface_descriptions.AvdInterfaceDescriptions

Bases: AvdFacts

Class used to render Interface Descriptions either from custom Jinja2 templates or using default Python Logic.

Since some templates might contain certain legacy variables (switch_), those are mapped from the switch. model

This class is imported adhoc based on the variable templates.interface_descriptions.python_module so it can be overridden by a custom python class.

Attributes starting with _ are internal and may change at any time.

Other attributes are “stable” and changes follow semver practices: - Existing attributes will not be changed in terms of type and value, but the underlying code for cached_properties may change. - New attributes may be added in minor releases. - The init method may change between minor versions as the data may need to be consumed from other sources. - Breaking changes may happen between major releases or in rare cases for bug fixes.

Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
class AvdInterfaceDescriptions(AvdFacts):
    """
    Class used to render Interface Descriptions either from custom Jinja2 templates or using default Python Logic.

    Since some templates might contain certain legacy variables (switch_*),
    those are mapped from the switch.* model

    This class is imported adhoc based on the variable `templates.interface_descriptions.python_module` so it can
    be overridden by a custom python class.

    Attributes starting with _ are internal and may change at any time.

    Other attributes are "stable" and changes follow semver practices:
    - Existing attributes will not be changed in terms of type and value, but the underlying code for cached_properties may change.
    - New attributes may be added in minor releases.
    - The __init__ method may change between minor versions as the data may need to be consumed from other sources.
    - Breaking changes may happen between major releases or in rare cases for bug fixes.
    """

    def _template(self, template_path: str, **kwargs: Any) -> str:
        template_vars = ChainMap(kwargs, self._hostvars)
        return self.shared_utils.template_var(template_path, template_vars)

    def underlay_ethernet_interface(self, data: InterfaceDescriptionData) -> str:
        """
        Build an underlay Ethernet interface description.

        If a jinja template is configured, use it.

        Available data:
            - link_type
            - description
            - peer
            - peer_interface
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type
            - vrf
            - wan_carrier
            - wan_circuit_id.
        """
        if template_path := self.shared_utils.interface_descriptions_templates.get("underlay_ethernet_interfaces"):
            return self._template(
                template_path,
                link={
                    "type": data.link_type,
                    "peer": data.peer,
                    "peer_interface": data.peer_interface,
                },
            )

        if data.description is not None:
            description = data.description
        elif data.link_type in ("underlay_p2p", "l3_edge", "core_interfaces"):
            description = self.shared_utils.default_underlay_p2p_ethernet_description
        elif data.link_type == "underlay_l2":
            description = self.shared_utils.underlay_l2_ethernet_description
        else:
            elems = [data.wan_carrier, data.wan_circuit_id, data.peer, data.peer_interface]
            description = "_".join([elem for elem in elems if elem])
            return f"{description}_vrf_{data.vrf}" if data.vrf is not None else description

        return AvdStringFormatter().format(
            description,
            **strip_null_from_data(
                {
                    "peer": data.peer,
                    "peer_interface": data.peer_interface,
                    "vrf": data.vrf,
                }
            ),
        )

    def underlay_port_channel_interface(self, data: InterfaceDescriptionData) -> str:
        """
        Build an underlay Port-Channel interface description.

        If a jinja template is configured, use it.

        Available data:
            - peer
            - peer_interface
            - peer_channel_group_id
            - peer_node_group
            - port_channel_id
            - port_channel_description
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type.
        """
        if template_path := self.shared_utils.interface_descriptions_templates.get("underlay_port_channel_interfaces"):
            return self._template(
                template_path,
                link={
                    "peer": data.peer,
                    "channel_group_id": data.port_channel_id,
                    "peer_channel_group_id": data.peer_channel_group_id,
                    "channel_description": data.port_channel_description,
                    "peer_node_group": data.peer_node_group,
                },
            )

        if data.port_channel_description is not None:
            description = data.port_channel_description
        elif data.link_type in ("l3_edge", "core_interfaces"):
            description = self.shared_utils.default_underlay_p2p_port_channel_description
        else:
            # This is for L2 port-channels
            description = self.shared_utils.underlay_l2_port_channel_description

        return AvdStringFormatter().format(
            description,
            **strip_null_from_data(
                {
                    "peer": data.peer,
                    "interface": data.interface,
                    "peer_interface": data.peer_interface,
                    "port_channel_id": data.port_channel_id,
                    "peer_port_channel_id": data.peer_channel_group_id,
                    "peer_node_group": data.peer_node_group,
                    "peer_node_group_or_peer": data.peer_node_group or data.peer,
                    "peer_node_group_or_uppercase_peer": data.peer_node_group or str(data.peer or "").upper() or None,
                }
            ),
        )

    def mlag_ethernet_interface(self, data: InterfaceDescriptionData) -> str:
        """
        Build an MLAG Ethernet interface description.

        If a jinja template is configured, use it.
        If not, use the default template as a format string template.

        Available data:
            - interface
            - peer_interface
            - mlag_peer
            - mlag_port_channel_id
            - mlag_peer_port_channel_id
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type.
        """
        if template_path := self.shared_utils.interface_descriptions_templates.get("mlag_ethernet_interfaces"):
            return self._template(
                template_path,
                mlag_interface=data.interface,
                mlag_peer=data.mlag_peer,
            )

        return AvdStringFormatter().format(
            self.shared_utils.mlag_member_description,
            **strip_null_from_data(
                {
                    "mlag_peer": data.mlag_peer,
                    "interface": data.interface,
                    "peer_interface": data.peer_interface,
                    "mlag_port_channel_id": data.mlag_port_channel_id,
                    "mlag_peer_port_channel_id": data.mlag_peer_port_channel_id,
                }
            ),
        )

    def mlag_port_channel_interface(self, data: InterfaceDescriptionData) -> str:
        """
        Build an MLAG Port-Channel interface description.

        If a jinja template is configured, use it.
        If not, use the default template as a format string template.

        Available data:
            - interface
            - peer_interface
            - mlag_peer
            - mlag_port_channel_id
            - mlag_peer_port_channel_id
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type.
        """
        if template_path := self.shared_utils.interface_descriptions_templates.get("mlag_port_channel_interfaces"):
            return self._template(
                template_path,
                mlag_interfaces=data.mlag_interfaces,
                mlag_peer=data.mlag_peer,
                mlag_port_channel_id=data.mlag_port_channel_id,
            )

        return AvdStringFormatter().format(
            self.shared_utils.mlag_port_channel_description,
            **strip_null_from_data(
                {
                    "mlag_peer": data.mlag_peer,
                    "interface": data.interface,
                    "peer_interface": data.peer_interface,
                    "mlag_port_channel_id": data.mlag_port_channel_id,
                    "mlag_peer_port_channel_id": data.mlag_peer_port_channel_id,
                }
            ),
        )

    def mlag_peer_svi(self, data: InterfaceDescriptionData) -> str:
        """
        Build an MLAG Peering SVI description.

        Available data:
            - interface
            - mlag_peer
            - mlag_peer_vlan
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type.
        """
        return AvdStringFormatter().format(
            self.shared_utils.mlag_peer_svi_description,
            **strip_null_from_data(
                {
                    "mlag_peer": data.mlag_peer,
                    "interface": data.interface,
                    "mlag_peer_vlan": data.mlag_peer_vlan,
                }
            ),
        )

    def mlag_peer_l3_svi(self, data: InterfaceDescriptionData) -> str:
        """
        Build an MLAG Peering SVI description.

        Available data:
            - interface
            - mlag_peer
            - mlag_peer_l3_vlan
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type.
        """
        return AvdStringFormatter().format(
            self.shared_utils.mlag_peer_l3_svi_description,
            **strip_null_from_data(
                {
                    "mlag_peer": data.mlag_peer,
                    "interface": data.interface,
                    "mlag_peer_l3_vlan": data.mlag_peer_l3_vlan,
                }
            ),
        )

    def mlag_peer_l3_vrf_svi(self, data: InterfaceDescriptionData) -> str:
        """
        Build an MLAG Peering SVI description.

        Available data:
            - interface
            - vlan
            - vrf
            - mlag_peer
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type.
        """
        return AvdStringFormatter().format(
            self.shared_utils.mlag_peer_l3_vrf_svi_description,
            **strip_null_from_data(
                {
                    "mlag_peer": data.mlag_peer,
                    "interface": data.interface,
                    "vlan": data.vlan,
                    "vrf": data.vrf,
                }
            ),
        )

    def connected_endpoints_ethernet_interface(self, data: InterfaceDescriptionData) -> str:
        """
        Build a connected endpoint Ethernet interface description.

        If a jinja template is configured, use it.
        If not, use the adapter.description as a format string template if set.
        Finally fall back to default templates depending on this being a network_port or not.

        Available data:
            - peer
            - peer_type
            - peer_interface
            - description
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type.
        """
        if template_path := self.shared_utils.interface_descriptions_templates.get("connected_endpoints_ethernet_interfaces"):
            return self._template(
                template_path,
                peer=data.peer,
                peer_interface=data.peer_interface,
                adapter_description=data.description,
                peer_type=data.peer_type,
            )

        if data.description:
            description = data.description
        elif data.peer_type == "network_port":
            description = self.shared_utils.default_network_ports_description
        else:
            description = self.shared_utils.default_connected_endpoints_description

        return AvdStringFormatter().format(
            description,
            **strip_null_from_data(
                {
                    "endpoint": data.peer,
                    "endpoint_port": data.peer_interface,
                    "endpoint_type": data.peer_type,
                    "port_channel_id": data.port_channel_id,
                }
            ),
        )

    def connected_endpoints_port_channel_interface(self, data: InterfaceDescriptionData) -> str:
        """
        Build a connected endpoint Port-channel description.

        If a jinja template is configured, use it.
        If not, use the port_channel.description as a format string template if set.
        Finally fall back to default templates depending on this being a network_port or not.

        Available data:
            - peer
            - peer_interface
            - peer_type
            - description
            - port_channel_id
            - port_channel_description
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type.
        """
        if template_path := self.shared_utils.interface_descriptions_templates.get("connected_endpoints_port_channel_interfaces"):
            return self._template(
                template_path,
                peer=data.peer,
                adapter_port_channel_id=data.port_channel_id,
                adapter_port_channel_description=data.port_channel_description,
                adapter_description=data.description,
            )

        if data.port_channel_description:
            port_channel_description = data.port_channel_description
        elif data.peer_type == "network_port":
            port_channel_description = self.shared_utils.default_network_ports_port_channel_description
        else:
            port_channel_description = self.shared_utils.default_connected_endpoints_port_channel_description

        # Template the adapter description in case it is being referenced in the port_channel_description
        adapter_description = (
            AvdStringFormatter().format(
                data.description,
                **strip_null_from_data(
                    {
                        "endpoint": data.peer,
                        "endpoint_port": data.peer_interface,
                        "endpoint_type": data.peer_type,
                    }
                ),
            )
            if data.description and "adapter_description" in port_channel_description
            else data.description
        )

        return AvdStringFormatter().format(
            port_channel_description,
            **strip_null_from_data(
                {
                    "endpoint": data.peer,
                    "endpoint_port_channel": data.peer_interface,
                    "endpoint_type": data.peer_type,
                    "port_channel_id": data.port_channel_id,
                    "adapter_description": adapter_description,
                    "adapter_description_or_endpoint": adapter_description or data.peer,
                }
            ),
        )

    def router_id_loopback_interface(self, data: InterfaceDescriptionData) -> str:
        """
        Build Router ID loopback interface description.

        Available data:
            - description
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type.
        """
        if template_path := default(
            self.shared_utils.interface_descriptions_templates.get("router_id_loopback_interface"),
            self.shared_utils.interface_descriptions_templates.get("overlay_loopback_interface"),
        ):
            return self._template(template_path, overlay_loopback_description=data.description, router_id_loopback_description=data.description)

        return data.description

    def vtep_loopback_interface(self, data: InterfaceDescriptionData) -> str:
        """
        Build VTEP loopback interface description.

        Available data:
            - description
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type
        """
        if template_path := self.shared_utils.interface_descriptions_templates.get("vtep_loopback_interface"):
            return self._template(template_path, vtep_loopback_description=data.description)

        return data.description

    def wan_ha_ethernet_interface(self, data: InterfaceDescriptionData) -> str:
        """
        Build WAN HA ethernet interface description.

        Available data:
            - interface
            - peer
            - peer_interface
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type
        """
        return f"WAN_HA_{data.peer}_{data.peer_interface}"

    def wan_ha_port_channel_interface(self, data: InterfaceDescriptionData) -> str:
        """
        Build WAN HA port-channel interface description.

        Available data:
            - interface
            - peer
            - peer_interface
            - mpls_overlay_role
            - mpls_lsr
            - overlay_routing_protocol
            - type
        """
        return f"WAN_HA_{data.peer}_{data.peer_interface}"

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.connected_endpoints_ethernet_interface(data)

Build a connected endpoint Ethernet interface description.

If a jinja template is configured, use it. If not, use the adapter.description as a format string template if set. Finally fall back to default templates depending on this being a network_port or not.

Available data
  • peer
  • peer_type
  • peer_interface
  • description
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type.
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def connected_endpoints_ethernet_interface(self, data: InterfaceDescriptionData) -> str:
    """
    Build a connected endpoint Ethernet interface description.

    If a jinja template is configured, use it.
    If not, use the adapter.description as a format string template if set.
    Finally fall back to default templates depending on this being a network_port or not.

    Available data:
        - peer
        - peer_type
        - peer_interface
        - description
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type.
    """
    if template_path := self.shared_utils.interface_descriptions_templates.get("connected_endpoints_ethernet_interfaces"):
        return self._template(
            template_path,
            peer=data.peer,
            peer_interface=data.peer_interface,
            adapter_description=data.description,
            peer_type=data.peer_type,
        )

    if data.description:
        description = data.description
    elif data.peer_type == "network_port":
        description = self.shared_utils.default_network_ports_description
    else:
        description = self.shared_utils.default_connected_endpoints_description

    return AvdStringFormatter().format(
        description,
        **strip_null_from_data(
            {
                "endpoint": data.peer,
                "endpoint_port": data.peer_interface,
                "endpoint_type": data.peer_type,
                "port_channel_id": data.port_channel_id,
            }
        ),
    )

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.connected_endpoints_port_channel_interface(data)

Build a connected endpoint Port-channel description.

If a jinja template is configured, use it. If not, use the port_channel.description as a format string template if set. Finally fall back to default templates depending on this being a network_port or not.

Available data
  • peer
  • peer_interface
  • peer_type
  • description
  • port_channel_id
  • port_channel_description
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type.
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def connected_endpoints_port_channel_interface(self, data: InterfaceDescriptionData) -> str:
    """
    Build a connected endpoint Port-channel description.

    If a jinja template is configured, use it.
    If not, use the port_channel.description as a format string template if set.
    Finally fall back to default templates depending on this being a network_port or not.

    Available data:
        - peer
        - peer_interface
        - peer_type
        - description
        - port_channel_id
        - port_channel_description
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type.
    """
    if template_path := self.shared_utils.interface_descriptions_templates.get("connected_endpoints_port_channel_interfaces"):
        return self._template(
            template_path,
            peer=data.peer,
            adapter_port_channel_id=data.port_channel_id,
            adapter_port_channel_description=data.port_channel_description,
            adapter_description=data.description,
        )

    if data.port_channel_description:
        port_channel_description = data.port_channel_description
    elif data.peer_type == "network_port":
        port_channel_description = self.shared_utils.default_network_ports_port_channel_description
    else:
        port_channel_description = self.shared_utils.default_connected_endpoints_port_channel_description

    # Template the adapter description in case it is being referenced in the port_channel_description
    adapter_description = (
        AvdStringFormatter().format(
            data.description,
            **strip_null_from_data(
                {
                    "endpoint": data.peer,
                    "endpoint_port": data.peer_interface,
                    "endpoint_type": data.peer_type,
                }
            ),
        )
        if data.description and "adapter_description" in port_channel_description
        else data.description
    )

    return AvdStringFormatter().format(
        port_channel_description,
        **strip_null_from_data(
            {
                "endpoint": data.peer,
                "endpoint_port_channel": data.peer_interface,
                "endpoint_type": data.peer_type,
                "port_channel_id": data.port_channel_id,
                "adapter_description": adapter_description,
                "adapter_description_or_endpoint": adapter_description or data.peer,
            }
        ),
    )

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.mlag_ethernet_interface(data)

Build an MLAG Ethernet interface description.

If a jinja template is configured, use it. If not, use the default template as a format string template.

Available data
  • interface
  • peer_interface
  • mlag_peer
  • mlag_port_channel_id
  • mlag_peer_port_channel_id
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type.
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def mlag_ethernet_interface(self, data: InterfaceDescriptionData) -> str:
    """
    Build an MLAG Ethernet interface description.

    If a jinja template is configured, use it.
    If not, use the default template as a format string template.

    Available data:
        - interface
        - peer_interface
        - mlag_peer
        - mlag_port_channel_id
        - mlag_peer_port_channel_id
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type.
    """
    if template_path := self.shared_utils.interface_descriptions_templates.get("mlag_ethernet_interfaces"):
        return self._template(
            template_path,
            mlag_interface=data.interface,
            mlag_peer=data.mlag_peer,
        )

    return AvdStringFormatter().format(
        self.shared_utils.mlag_member_description,
        **strip_null_from_data(
            {
                "mlag_peer": data.mlag_peer,
                "interface": data.interface,
                "peer_interface": data.peer_interface,
                "mlag_port_channel_id": data.mlag_port_channel_id,
                "mlag_peer_port_channel_id": data.mlag_peer_port_channel_id,
            }
        ),
    )

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.mlag_peer_l3_svi(data)

Build an MLAG Peering SVI description.

Available data
  • interface
  • mlag_peer
  • mlag_peer_l3_vlan
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type.
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def mlag_peer_l3_svi(self, data: InterfaceDescriptionData) -> str:
    """
    Build an MLAG Peering SVI description.

    Available data:
        - interface
        - mlag_peer
        - mlag_peer_l3_vlan
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type.
    """
    return AvdStringFormatter().format(
        self.shared_utils.mlag_peer_l3_svi_description,
        **strip_null_from_data(
            {
                "mlag_peer": data.mlag_peer,
                "interface": data.interface,
                "mlag_peer_l3_vlan": data.mlag_peer_l3_vlan,
            }
        ),
    )

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.mlag_peer_l3_vrf_svi(data)

Build an MLAG Peering SVI description.

Available data
  • interface
  • vlan
  • vrf
  • mlag_peer
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type.
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def mlag_peer_l3_vrf_svi(self, data: InterfaceDescriptionData) -> str:
    """
    Build an MLAG Peering SVI description.

    Available data:
        - interface
        - vlan
        - vrf
        - mlag_peer
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type.
    """
    return AvdStringFormatter().format(
        self.shared_utils.mlag_peer_l3_vrf_svi_description,
        **strip_null_from_data(
            {
                "mlag_peer": data.mlag_peer,
                "interface": data.interface,
                "vlan": data.vlan,
                "vrf": data.vrf,
            }
        ),
    )

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.mlag_peer_svi(data)

Build an MLAG Peering SVI description.

Available data
  • interface
  • mlag_peer
  • mlag_peer_vlan
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type.
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def mlag_peer_svi(self, data: InterfaceDescriptionData) -> str:
    """
    Build an MLAG Peering SVI description.

    Available data:
        - interface
        - mlag_peer
        - mlag_peer_vlan
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type.
    """
    return AvdStringFormatter().format(
        self.shared_utils.mlag_peer_svi_description,
        **strip_null_from_data(
            {
                "mlag_peer": data.mlag_peer,
                "interface": data.interface,
                "mlag_peer_vlan": data.mlag_peer_vlan,
            }
        ),
    )

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.mlag_port_channel_interface(data)

Build an MLAG Port-Channel interface description.

If a jinja template is configured, use it. If not, use the default template as a format string template.

Available data
  • interface
  • peer_interface
  • mlag_peer
  • mlag_port_channel_id
  • mlag_peer_port_channel_id
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type.
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def mlag_port_channel_interface(self, data: InterfaceDescriptionData) -> str:
    """
    Build an MLAG Port-Channel interface description.

    If a jinja template is configured, use it.
    If not, use the default template as a format string template.

    Available data:
        - interface
        - peer_interface
        - mlag_peer
        - mlag_port_channel_id
        - mlag_peer_port_channel_id
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type.
    """
    if template_path := self.shared_utils.interface_descriptions_templates.get("mlag_port_channel_interfaces"):
        return self._template(
            template_path,
            mlag_interfaces=data.mlag_interfaces,
            mlag_peer=data.mlag_peer,
            mlag_port_channel_id=data.mlag_port_channel_id,
        )

    return AvdStringFormatter().format(
        self.shared_utils.mlag_port_channel_description,
        **strip_null_from_data(
            {
                "mlag_peer": data.mlag_peer,
                "interface": data.interface,
                "peer_interface": data.peer_interface,
                "mlag_port_channel_id": data.mlag_port_channel_id,
                "mlag_peer_port_channel_id": data.mlag_peer_port_channel_id,
            }
        ),
    )

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.router_id_loopback_interface(data)

Build Router ID loopback interface description.

Available data
  • description
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type.
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def router_id_loopback_interface(self, data: InterfaceDescriptionData) -> str:
    """
    Build Router ID loopback interface description.

    Available data:
        - description
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type.
    """
    if template_path := default(
        self.shared_utils.interface_descriptions_templates.get("router_id_loopback_interface"),
        self.shared_utils.interface_descriptions_templates.get("overlay_loopback_interface"),
    ):
        return self._template(template_path, overlay_loopback_description=data.description, router_id_loopback_description=data.description)

    return data.description

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.underlay_ethernet_interface(data)

Build an underlay Ethernet interface description.

If a jinja template is configured, use it.

Available data
  • link_type
  • description
  • peer
  • peer_interface
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type
  • vrf
  • wan_carrier
  • wan_circuit_id.
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def underlay_ethernet_interface(self, data: InterfaceDescriptionData) -> str:
    """
    Build an underlay Ethernet interface description.

    If a jinja template is configured, use it.

    Available data:
        - link_type
        - description
        - peer
        - peer_interface
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type
        - vrf
        - wan_carrier
        - wan_circuit_id.
    """
    if template_path := self.shared_utils.interface_descriptions_templates.get("underlay_ethernet_interfaces"):
        return self._template(
            template_path,
            link={
                "type": data.link_type,
                "peer": data.peer,
                "peer_interface": data.peer_interface,
            },
        )

    if data.description is not None:
        description = data.description
    elif data.link_type in ("underlay_p2p", "l3_edge", "core_interfaces"):
        description = self.shared_utils.default_underlay_p2p_ethernet_description
    elif data.link_type == "underlay_l2":
        description = self.shared_utils.underlay_l2_ethernet_description
    else:
        elems = [data.wan_carrier, data.wan_circuit_id, data.peer, data.peer_interface]
        description = "_".join([elem for elem in elems if elem])
        return f"{description}_vrf_{data.vrf}" if data.vrf is not None else description

    return AvdStringFormatter().format(
        description,
        **strip_null_from_data(
            {
                "peer": data.peer,
                "peer_interface": data.peer_interface,
                "vrf": data.vrf,
            }
        ),
    )

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.underlay_port_channel_interface(data)

Build an underlay Port-Channel interface description.

If a jinja template is configured, use it.

Available data
  • peer
  • peer_interface
  • peer_channel_group_id
  • peer_node_group
  • port_channel_id
  • port_channel_description
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type.
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def underlay_port_channel_interface(self, data: InterfaceDescriptionData) -> str:
    """
    Build an underlay Port-Channel interface description.

    If a jinja template is configured, use it.

    Available data:
        - peer
        - peer_interface
        - peer_channel_group_id
        - peer_node_group
        - port_channel_id
        - port_channel_description
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type.
    """
    if template_path := self.shared_utils.interface_descriptions_templates.get("underlay_port_channel_interfaces"):
        return self._template(
            template_path,
            link={
                "peer": data.peer,
                "channel_group_id": data.port_channel_id,
                "peer_channel_group_id": data.peer_channel_group_id,
                "channel_description": data.port_channel_description,
                "peer_node_group": data.peer_node_group,
            },
        )

    if data.port_channel_description is not None:
        description = data.port_channel_description
    elif data.link_type in ("l3_edge", "core_interfaces"):
        description = self.shared_utils.default_underlay_p2p_port_channel_description
    else:
        # This is for L2 port-channels
        description = self.shared_utils.underlay_l2_port_channel_description

    return AvdStringFormatter().format(
        description,
        **strip_null_from_data(
            {
                "peer": data.peer,
                "interface": data.interface,
                "peer_interface": data.peer_interface,
                "port_channel_id": data.port_channel_id,
                "peer_port_channel_id": data.peer_channel_group_id,
                "peer_node_group": data.peer_node_group,
                "peer_node_group_or_peer": data.peer_node_group or data.peer,
                "peer_node_group_or_uppercase_peer": data.peer_node_group or str(data.peer or "").upper() or None,
            }
        ),
    )

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.vtep_loopback_interface(data)

Build VTEP loopback interface description.

Available data
  • description
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def vtep_loopback_interface(self, data: InterfaceDescriptionData) -> str:
    """
    Build VTEP loopback interface description.

    Available data:
        - description
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type
    """
    if template_path := self.shared_utils.interface_descriptions_templates.get("vtep_loopback_interface"):
        return self._template(template_path, vtep_loopback_description=data.description)

    return data.description

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.wan_ha_ethernet_interface(data)

Build WAN HA ethernet interface description.

Available data
  • interface
  • peer
  • peer_interface
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def wan_ha_ethernet_interface(self, data: InterfaceDescriptionData) -> str:
    """
    Build WAN HA ethernet interface description.

    Available data:
        - interface
        - peer
        - peer_interface
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type
    """
    return f"WAN_HA_{data.peer}_{data.peer_interface}"

pyavd.api.interface_descriptions.AvdInterfaceDescriptions.wan_ha_port_channel_interface(data)

Build WAN HA port-channel interface description.

Available data
  • interface
  • peer
  • peer_interface
  • mpls_overlay_role
  • mpls_lsr
  • overlay_routing_protocol
  • type
Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
def wan_ha_port_channel_interface(self, data: InterfaceDescriptionData) -> str:
    """
    Build WAN HA port-channel interface description.

    Available data:
        - interface
        - peer
        - peer_interface
        - mpls_overlay_role
        - mpls_lsr
        - overlay_routing_protocol
        - type
    """
    return f"WAN_HA_{data.peer}_{data.peer_interface}"

pyavd.api.interface_descriptions.InterfaceDescriptionData

This class is used as transport of data between AVD code and instances of AvdInterfaceDescriptions class or subclasses hereof.

Attributes starting with _ are internal and may change at any time.

Other attributes are “stable” and changes follow semver practices: - Existing attributes will not be changed in terms of type and value, but the underlying code for cached_properties may change. - New attributes may be added in minor releases. - The init method may change between minor versions as the data may need to be consumed from other sources. - Breaking changes may happen between major releases.

Source code in python-avd/pyavd/api/interface_descriptions/__init__.py
class InterfaceDescriptionData:
    """
    This class is used as transport of data between AVD code and instances of AvdInterfaceDescriptions class or subclasses hereof.

    Attributes starting with _ are internal and may change at any time.

    Other attributes are "stable" and changes follow semver practices:
    - Existing attributes will not be changed in terms of type and value, but the underlying code for cached_properties may change.
    - New attributes may be added in minor releases.
    - The __init__ method may change between minor versions as the data may need to be consumed from other sources.
    - Breaking changes may happen between major releases.
    """

    _shared_utils: SharedUtils
    description: str | None
    """Set description for interface"""
    interface: str | None
    """Local interface"""
    link_type: str | None
    """Type of connection. Like 'underlay_p2p' or 'underlay_l2'"""
    peer: str | None
    """Hostname of peer"""
    peer_interface: str | None
    """Interface of peer"""
    peer_channel_group_id: int | None
    """Port channel ID of peer"""
    peer_node_group: str | None
    """Node group of peer"""
    peer_type: str | None
    """Type of peer"""
    port_channel_id: int | None
    """Port channel ID"""
    port_channel_description: str | None
    """Set description for port-channel"""
    vlan: int | None
    """VLAN ID"""
    vrf: str | None
    """Interface VRF"""
    wan_carrier: str | None
    """The WAN Carrier this interface is connected to"""
    wan_circuit_id: str | None
    """The WAN Circuit ID for this interface."""

    def __init__(
        self,
        shared_utils: SharedUtils,
        description: str | None = None,
        interface: str | None = None,
        link_type: str | None = None,
        peer: str | None = None,
        peer_interface: str | None = None,
        peer_channel_group_id: int | None = None,
        peer_node_group: str | None = None,
        peer_type: str | None = None,
        port_channel_id: int | None = None,
        port_channel_description: str | None = None,
        vlan: int | None = None,
        vrf: str | None = None,
        wan_carrier: str | None = None,
        wan_circuit_id: str | None = None,
    ) -> None:
        self._shared_utils = shared_utils
        self.description = description
        self.interface = interface
        self.link_type = link_type
        self.peer = peer
        self.peer_interface = peer_interface
        self.peer_channel_group_id = peer_channel_group_id
        self.peer_node_group = peer_node_group
        self.peer_type = peer_type
        self.port_channel_id = port_channel_id
        self.port_channel_description = port_channel_description
        self.vlan = vlan
        self.vrf = vrf
        self.wan_carrier = wan_carrier
        self.wan_circuit_id = wan_circuit_id

    @property
    def mpls_overlay_role(self) -> str | None:
        return self._shared_utils.mpls_overlay_role

    @property
    def overlay_routing_protocol(self) -> str:
        return self._shared_utils.overlay_routing_protocol

    @property
    def mlag_interfaces(self) -> list:
        return self._shared_utils.mlag_interfaces

    @property
    def mlag_peer(self) -> str:
        return self._shared_utils.mlag_peer

    @property
    def mlag_port_channel_id(self) -> int:
        return self._shared_utils.mlag_port_channel_id

    @property
    def mlag_peer_port_channel_id(self) -> int:
        return self._shared_utils.mlag_peer_port_channel_id

    @property
    def mlag_peer_vlan(self) -> int:
        return self._shared_utils.mlag_peer_vlan

    @property
    def mlag_peer_l3_vlan(self) -> int | None:
        return self._shared_utils.mlag_peer_l3_vlan

    @property
    def mpls_lsr(self) -> bool:
        return self._shared_utils.mpls_lsr

    @property
    def type(self) -> str:
        return self._shared_utils.type

pyavd.api.interface_descriptions.InterfaceDescriptionData.description: str | None = description instance-attribute

Set description for interface

pyavd.api.interface_descriptions.InterfaceDescriptionData.interface: str | None = interface instance-attribute

Local interface

Type of connection. Like ‘underlay_p2p’ or ‘underlay_l2’

pyavd.api.interface_descriptions.InterfaceDescriptionData.peer: str | None = peer instance-attribute

Hostname of peer

pyavd.api.interface_descriptions.InterfaceDescriptionData.peer_channel_group_id: int | None = peer_channel_group_id instance-attribute

Port channel ID of peer

pyavd.api.interface_descriptions.InterfaceDescriptionData.peer_interface: str | None = peer_interface instance-attribute

Interface of peer

pyavd.api.interface_descriptions.InterfaceDescriptionData.peer_node_group: str | None = peer_node_group instance-attribute

Node group of peer

pyavd.api.interface_descriptions.InterfaceDescriptionData.peer_type: str | None = peer_type instance-attribute

Type of peer

pyavd.api.interface_descriptions.InterfaceDescriptionData.port_channel_description: str | None = port_channel_description instance-attribute

Set description for port-channel

pyavd.api.interface_descriptions.InterfaceDescriptionData.port_channel_id: int | None = port_channel_id instance-attribute

Port channel ID

pyavd.api.interface_descriptions.InterfaceDescriptionData.vlan: int | None = vlan instance-attribute

VLAN ID

pyavd.api.interface_descriptions.InterfaceDescriptionData.vrf: str | None = vrf instance-attribute

Interface VRF

pyavd.api.interface_descriptions.InterfaceDescriptionData.wan_carrier: str | None = wan_carrier instance-attribute

The WAN Carrier this interface is connected to

pyavd.api.interface_descriptions.InterfaceDescriptionData.wan_circuit_id: str | None = wan_circuit_id instance-attribute

The WAN Circuit ID for this interface.

pyavd.api.ip_addressing.AvdIpAddressing

Bases: AvdFacts, UtilsMixin

Class used to render IP addresses either from custom Jinja2 templates or using default Python Logic.

Since some templates might contain certain legacy variables (switch_), those are mapped from the switch. model

This class is imported adhoc based on the variable templates.ip_addressing.python_module so it can be overridden by a custom python class.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
class AvdIpAddressing(AvdFacts, UtilsMixin):
    """
    Class used to render IP addresses either from custom Jinja2 templates or using default Python Logic.

    Since some templates might contain certain legacy variables (switch_*),
    those are mapped from the switch.* model

    This class is imported adhoc based on the variable `templates.ip_addressing.python_module` so it can
    be overridden by a custom python class.
    """

    def _ip(self, pool: str, prefixlen: int, subnet_offset: int, ip_offset: int) -> str:
        """Shortcut to get_ip_from_pool in case any custom subclasses are using this."""
        return get_ip_from_pool(pool, prefixlen, subnet_offset, ip_offset)

    def _template(self, template_path: str, **kwargs: Any) -> str:
        template_vars = ChainMap(kwargs, self._hostvars)
        return self.shared_utils.template_var(template_path, template_vars)

    def _mlag_ip(self, pool: str, ip_offset: int, address_family: str = "ipv4") -> str:
        """
        Different addressing algorithms.

            - first_id: offset from pool is `(mlag_primary_id - 1) * 2`
            - odd_id: offset from pool is `(odd_id - 1) * 2`. Requires MLAG pair to have a node with odd and a node with an even ID
            - same_subnet: offset from pool is always 0. All MLAG pairs will be using the same subnet (default /31).
              Requires the pool to have the same prefix length.
        """
        prefixlen = self._fabric_ip_addressing_mlag_ipv6_prefix_length if address_family == "ipv6" else self._fabric_ip_addressing_mlag_ipv4_prefix_length
        if self._fabric_ipaddress_mlag_algorithm == "odd_id":
            offset = self._mlag_odd_id_based_offset
            return get_ip_from_pool(pool, prefixlen, offset, ip_offset)

        if self._fabric_ipaddress_mlag_algorithm == "same_subnet":
            pool_network = ipaddress.ip_network(pool, strict=False)
            if pool_network.prefixlen != prefixlen:
                msg = f"MLAG same_subnet addressing requires the pool to be a /{prefixlen}"
                raise AristaAvdError(msg)
            return get_ip_from_pool(pool, prefixlen, 0, ip_offset)

        # Use default first_id
        offset = self._mlag_primary_id - 1
        return get_ip_from_pool(pool, prefixlen, offset, ip_offset)

    def mlag_ibgp_peering_ip_primary(self, mlag_ibgp_peering_ipv4_pool: str) -> str:
        """Return IP for L3 Peerings in VRFs for MLAG Primary."""
        if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ibgp_peering_ip_primary"):
            return self._template(
                template_path,
                vrf={"mlag_ibgp_peering_ipv4_pool": mlag_ibgp_peering_ipv4_pool},
            )

        return self._mlag_ip(mlag_ibgp_peering_ipv4_pool, 0)

    def mlag_ibgp_peering_ip_secondary(self, mlag_ibgp_peering_ipv4_pool: str) -> str:
        """Return IP for L3 Peerings in VRFs for MLAG Secondary."""
        if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ibgp_peering_ip_secondary"):
            return self._template(
                template_path,
                vrf={"mlag_ibgp_peering_ipv4_pool": mlag_ibgp_peering_ipv4_pool},
            )

        return self._mlag_ip(mlag_ibgp_peering_ipv4_pool, 1)

    def mlag_ip_primary(self) -> str:
        """
        Return IP for MLAG Primary.

        Default pool is "mlag_peer_ipv4_pool"
        """
        if self.shared_utils.mlag_peer_address_family == "ipv6":
            if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ip_primary"):
                return self._template(
                    template_path,
                    mlag_primary_id=self._mlag_primary_id,
                    mlag_secondary_id=self._mlag_secondary_id,
                    switch_data={"combined": {"mlag_peer_ipv6_pool": self._mlag_peer_ipv6_pool}},
                )

            return self._mlag_ip(self._mlag_peer_ipv6_pool, 0, self.shared_utils.mlag_peer_address_family)

        if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ip_primary"):
            return self._template(
                template_path,
                mlag_primary_id=self._mlag_primary_id,
                mlag_secondary_id=self._mlag_secondary_id,
                switch_data={"combined": {"mlag_peer_ipv4_pool": self._mlag_peer_ipv4_pool}},
            )

        return self._mlag_ip(self._mlag_peer_ipv4_pool, 0)

    def mlag_ip_secondary(self) -> str:
        """
        Return IP for MLAG Secondary.

        Default pool is "mlag_peer_ipv4_pool"
        """
        if self.shared_utils.mlag_peer_address_family == "ipv6":
            if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ip_secondary"):
                return self._template(
                    template_path,
                    mlag_primary_id=self._mlag_primary_id,
                    mlag_secondary_id=self._mlag_secondary_id,
                    switch_data={"combined": {"mlag_peer_ipv6_pool": self._mlag_peer_ipv6_pool}},
                )

            return self._mlag_ip(self._mlag_peer_ipv6_pool, 1, self.shared_utils.mlag_peer_address_family)

        if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ip_secondary"):
            return self._template(
                template_path,
                mlag_primary_id=self._mlag_primary_id,
                mlag_secondary_id=self._mlag_secondary_id,
                switch_data={"combined": {"mlag_peer_ipv4_pool": self._mlag_peer_ipv4_pool}},
            )

        return self._mlag_ip(self._mlag_peer_ipv4_pool, 1)

    def mlag_l3_ip_primary(self) -> str:
        """
        Return IP for L3 Peerings for MLAG Primary.

        Default pool is "mlag_peer_l3_ipv4_pool"
        """
        if template_path := self.shared_utils.ip_addressing_templates.get("mlag_l3_ip_primary"):
            return self._template(
                template_path,
                mlag_primary_id=self._mlag_primary_id,
                mlag_secondary_id=self._mlag_secondary_id,
                switch_data={"combined": {"mlag_peer_l3_ipv4_pool": self._mlag_peer_l3_ipv4_pool}},
            )

        return self._mlag_ip(self._mlag_peer_l3_ipv4_pool, 0)

    def mlag_l3_ip_secondary(self) -> str:
        """
        Return IP for L3 Peerings for MLAG Secondary.

        Default pool is "mlag_peer_l3_ipv4_pool"
        """
        if template_path := self.shared_utils.ip_addressing_templates.get("mlag_l3_ip_secondary"):
            return self._template(
                template_path,
                mlag_primary_id=self._mlag_primary_id,
                mlag_secondary_id=self._mlag_secondary_id,
                switch_data={"combined": {"mlag_peer_l3_ipv4_pool": self._mlag_peer_l3_ipv4_pool}},
            )

        return self._mlag_ip(self._mlag_peer_l3_ipv4_pool, 1)

    def p2p_uplinks_ip(self, uplink_switch_index: int) -> str:
        """Return Child IP for P2P Uplinks."""
        uplink_switch_index = int(uplink_switch_index)
        if template_path := self.shared_utils.ip_addressing_templates.get("p2p_uplinks_ip"):
            return self._template(
                template_path,
                uplink_switch_index=uplink_switch_index,
            )

        prefixlen = self._fabric_ip_addressing_p2p_uplinks_ipv4_prefix_length
        p2p_ipv4_pool, offset = self._get_p2p_ipv4_pool_and_offset(uplink_switch_index)

        return get_ip_from_pool(p2p_ipv4_pool, prefixlen, offset, 1)

    def p2p_uplinks_peer_ip(self, uplink_switch_index: int) -> str:
        """Return Parent IP for P2P Uplinks."""
        uplink_switch_index = int(uplink_switch_index)
        if template_path := self.shared_utils.ip_addressing_templates.get("p2p_uplinks_peer_ip"):
            return self._template(
                template_path,
                uplink_switch_index=uplink_switch_index,
            )

        prefixlen = self._fabric_ip_addressing_p2p_uplinks_ipv4_prefix_length
        p2p_ipv4_pool, offset = self._get_p2p_ipv4_pool_and_offset(uplink_switch_index)

        return get_ip_from_pool(p2p_ipv4_pool, prefixlen, offset, 0)

    def p2p_vrfs_uplinks_ip(
        self,
        uplink_switch_index: int,
        vrf: str,  # pylint: disable=unused-argument # NOSONAR # noqa: ARG002
    ) -> str:
        """
        Return Child IP for P2P-VRFs Uplinks.

        Unless overridden in a custom IP addressing module, this will just reuse the regular ip addressing logic.
        """
        return self.p2p_uplinks_ip(uplink_switch_index)

    def p2p_vrfs_uplinks_peer_ip(
        self,
        uplink_switch_index: int,
        vrf: str,  # pylint: disable=unused-argument # NOSONAR # noqa: ARG002
    ) -> str:
        """
        Return Parent IP for P2P-VRFs Uplinks.

        Unless overridden in a custom IP addressing module, this will just reuse the regular ip addressing logic.
        """
        return self.p2p_uplinks_peer_ip(uplink_switch_index)

    def router_id(self) -> str:
        """
        Return IP address for Router ID.

        If "loopback_ipv4_address" is set, it is used.
        Default pool is "loopback_ipv4_pool"
        Default offset from pool is `id + loopback_ipv4_offset`
        """
        if self._loopback_ipv4_address:
            return self._loopback_ipv4_address

        if template_path := self.shared_utils.ip_addressing_templates.get("router_id"):
            return self._template(
                template_path,
                switch_id=self._id,
                loopback_ipv4_pool=self._loopback_ipv4_pool,
                loopback_ipv4_offset=self._loopback_ipv4_offset,
            )

        offset = self._id + self._loopback_ipv4_offset
        return get_ip_from_pool(self._loopback_ipv4_pool, 32, offset, 0)

    def ipv6_router_id(self) -> str:
        """
        Return IPv6 address for Router ID.

        Default pool is "loopback_ipv6_pool"
        Default offset from pool is `id + loopback_ipv6_offset`
        """
        if template_path := self.shared_utils.ip_addressing_templates.get("ipv6_router_id"):
            return self._template(
                template_path,
                switch_id=self._id,
                loopback_ipv6_pool=self._loopback_ipv6_pool,
                loopback_ipv6_offset=self._loopback_ipv6_offset,
            )

        offset = self._id + self._loopback_ipv6_offset
        return get_ip_from_pool(self._loopback_ipv6_pool, 128, offset, 0)

    def vtep_ip_mlag(self) -> str:
        """
        Return IP address for VTEP for MLAG Leaf.

        If "vtep_loopback_ipv4_address" is set, it is used.
        Default pool is "vtep_loopback_ipv4_pool"
        Default offset from pool is `mlag_primary_id + loopback_ipv4_offset`
        """
        if self._vtep_loopback_ipv4_address:
            return self._vtep_loopback_ipv4_address

        if template_path := self.shared_utils.ip_addressing_templates.get("vtep_ip_mlag"):
            return self._template(
                template_path,
                switch_id=self._id,
                switch_vtep_loopback_ipv4_pool=self._vtep_loopback_ipv4_pool,
                loopback_ipv4_offset=self._loopback_ipv4_offset,
                mlag_primary_id=self._mlag_primary_id,
                mlag_secondary_id=self._mlag_secondary_id,
            )

        offset = self._mlag_primary_id + self._loopback_ipv4_offset
        return get_ip_from_pool(self._vtep_loopback_ipv4_pool, 32, offset, 0)

    def vtep_ip(self) -> str:
        """
        Return IP address for VTEP.

        If "vtep_loopback_ipv4_address" is set, it is used.
        Default pool is "vtep_loopback_ipv4_pool"
        Default offset from pool is `id + loopback_ipv4_offset`
        """
        if self._vtep_loopback_ipv4_address:
            return self._vtep_loopback_ipv4_address

        if template_path := self.shared_utils.ip_addressing_templates.get("vtep_ip"):
            return self._template(
                template_path,
                switch_id=self._id,
                switch_vtep_loopback_ipv4_pool=self._vtep_loopback_ipv4_pool,
                loopback_ipv4_offset=self._loopback_ipv4_offset,
            )

        offset = self._id + self._loopback_ipv4_offset
        return get_ip_from_pool(self._vtep_loopback_ipv4_pool, 32, offset, 0)

    def vrf_loopback_ip(self, pool: str) -> str:
        """
        Return IP address for a Loopback interface based on the given pool.

        Default offset from pool is `id + loopback_ipv4_offset`.

        Used for "vtep_diagnostic.loopback".
        """
        offset = self.shared_utils.id + self.shared_utils.loopback_ipv4_offset
        return get_ip_from_pool(pool, 32, offset, 0)

    def vrf_loopback_ipv6(self, pool: str) -> str:
        """
        Return IPv6 address for a Loopback interface based on the given pool.

        Default offset from pool is `id + loopback_ipv6_offset`.

        Used for "vtep_diagnostic.loopback".
        """
        offset = self.shared_utils.id + self.shared_utils.loopback_ipv6_offset
        return get_ip_from_pool(pool, 128, offset, 0)

    def evpn_underlay_l3_multicast_group(
        self,
        underlay_l3_multicast_group_ipv4_pool: str,
        vrf_vni: int,  # pylint: disable=unused-argument # noqa: ARG002
        vrf_id: int,
        evpn_underlay_l3_multicast_group_ipv4_pool_offset: int,
    ) -> str:
        """Return IP address to be used for EVPN underlay L3 multicast group."""
        offset = vrf_id - 1 + evpn_underlay_l3_multicast_group_ipv4_pool_offset
        return get_ip_from_pool(underlay_l3_multicast_group_ipv4_pool, 32, offset, 0)

    def evpn_underlay_l2_multicast_group(
        self,
        underlay_l2_multicast_group_ipv4_pool: str,
        vlan_id: int,
        underlay_l2_multicast_group_ipv4_pool_offset: int,
    ) -> str:
        """Return IP address to be used for EVPN underlay L2 multicast group."""
        offset = vlan_id - 1 + underlay_l2_multicast_group_ipv4_pool_offset
        return get_ip_from_pool(underlay_l2_multicast_group_ipv4_pool, 32, offset, 0)

    def wan_ha_ip(self) -> str:
        """Return the WAN HA local IP address."""
        wan_ha_ipv4_pool = self.shared_utils.wan_ha_ipv4_pool
        prefixlen = self.shared_utils.fabric_ip_addressing_wan_ha_ipv4_prefix_length

        if self.shared_utils.is_first_ha_peer:
            ip_address = get_ip_from_pool(wan_ha_ipv4_pool, prefixlen, 0, 0)
        else:
            ip_address = get_ip_from_pool(wan_ha_ipv4_pool, prefixlen, 0, 1)

        return f"{ip_address}/{prefixlen}"

    def wan_ha_peer_ip(self) -> str:
        """Return the WAN HA peer IP."""
        wan_ha_ipv4_pool = self.shared_utils.wan_ha_ipv4_pool
        prefixlen = self.shared_utils.fabric_ip_addressing_wan_ha_ipv4_prefix_length

        if self.shared_utils.is_first_ha_peer:
            ip_address = get_ip_from_pool(wan_ha_ipv4_pool, prefixlen, 0, 1)
        else:
            ip_address = get_ip_from_pool(wan_ha_ipv4_pool, prefixlen, 0, 0)

        return f"{ip_address}/{prefixlen}"

pyavd.api.ip_addressing.AvdIpAddressing.evpn_underlay_l2_multicast_group(underlay_l2_multicast_group_ipv4_pool, vlan_id, underlay_l2_multicast_group_ipv4_pool_offset)

Return IP address to be used for EVPN underlay L2 multicast group.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def evpn_underlay_l2_multicast_group(
    self,
    underlay_l2_multicast_group_ipv4_pool: str,
    vlan_id: int,
    underlay_l2_multicast_group_ipv4_pool_offset: int,
) -> str:
    """Return IP address to be used for EVPN underlay L2 multicast group."""
    offset = vlan_id - 1 + underlay_l2_multicast_group_ipv4_pool_offset
    return get_ip_from_pool(underlay_l2_multicast_group_ipv4_pool, 32, offset, 0)

pyavd.api.ip_addressing.AvdIpAddressing.evpn_underlay_l3_multicast_group(underlay_l3_multicast_group_ipv4_pool, vrf_vni, vrf_id, evpn_underlay_l3_multicast_group_ipv4_pool_offset)

Return IP address to be used for EVPN underlay L3 multicast group.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def evpn_underlay_l3_multicast_group(
    self,
    underlay_l3_multicast_group_ipv4_pool: str,
    vrf_vni: int,  # pylint: disable=unused-argument # noqa: ARG002
    vrf_id: int,
    evpn_underlay_l3_multicast_group_ipv4_pool_offset: int,
) -> str:
    """Return IP address to be used for EVPN underlay L3 multicast group."""
    offset = vrf_id - 1 + evpn_underlay_l3_multicast_group_ipv4_pool_offset
    return get_ip_from_pool(underlay_l3_multicast_group_ipv4_pool, 32, offset, 0)

pyavd.api.ip_addressing.AvdIpAddressing.ipv6_router_id()

Return IPv6 address for Router ID.

Default pool is “loopback_ipv6_pool” Default offset from pool is id + loopback_ipv6_offset

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def ipv6_router_id(self) -> str:
    """
    Return IPv6 address for Router ID.

    Default pool is "loopback_ipv6_pool"
    Default offset from pool is `id + loopback_ipv6_offset`
    """
    if template_path := self.shared_utils.ip_addressing_templates.get("ipv6_router_id"):
        return self._template(
            template_path,
            switch_id=self._id,
            loopback_ipv6_pool=self._loopback_ipv6_pool,
            loopback_ipv6_offset=self._loopback_ipv6_offset,
        )

    offset = self._id + self._loopback_ipv6_offset
    return get_ip_from_pool(self._loopback_ipv6_pool, 128, offset, 0)

pyavd.api.ip_addressing.AvdIpAddressing.mlag_ibgp_peering_ip_primary(mlag_ibgp_peering_ipv4_pool)

Return IP for L3 Peerings in VRFs for MLAG Primary.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def mlag_ibgp_peering_ip_primary(self, mlag_ibgp_peering_ipv4_pool: str) -> str:
    """Return IP for L3 Peerings in VRFs for MLAG Primary."""
    if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ibgp_peering_ip_primary"):
        return self._template(
            template_path,
            vrf={"mlag_ibgp_peering_ipv4_pool": mlag_ibgp_peering_ipv4_pool},
        )

    return self._mlag_ip(mlag_ibgp_peering_ipv4_pool, 0)

pyavd.api.ip_addressing.AvdIpAddressing.mlag_ibgp_peering_ip_secondary(mlag_ibgp_peering_ipv4_pool)

Return IP for L3 Peerings in VRFs for MLAG Secondary.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def mlag_ibgp_peering_ip_secondary(self, mlag_ibgp_peering_ipv4_pool: str) -> str:
    """Return IP for L3 Peerings in VRFs for MLAG Secondary."""
    if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ibgp_peering_ip_secondary"):
        return self._template(
            template_path,
            vrf={"mlag_ibgp_peering_ipv4_pool": mlag_ibgp_peering_ipv4_pool},
        )

    return self._mlag_ip(mlag_ibgp_peering_ipv4_pool, 1)

pyavd.api.ip_addressing.AvdIpAddressing.mlag_ip_primary()

Return IP for MLAG Primary.

Default pool is “mlag_peer_ipv4_pool”

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def mlag_ip_primary(self) -> str:
    """
    Return IP for MLAG Primary.

    Default pool is "mlag_peer_ipv4_pool"
    """
    if self.shared_utils.mlag_peer_address_family == "ipv6":
        if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ip_primary"):
            return self._template(
                template_path,
                mlag_primary_id=self._mlag_primary_id,
                mlag_secondary_id=self._mlag_secondary_id,
                switch_data={"combined": {"mlag_peer_ipv6_pool": self._mlag_peer_ipv6_pool}},
            )

        return self._mlag_ip(self._mlag_peer_ipv6_pool, 0, self.shared_utils.mlag_peer_address_family)

    if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ip_primary"):
        return self._template(
            template_path,
            mlag_primary_id=self._mlag_primary_id,
            mlag_secondary_id=self._mlag_secondary_id,
            switch_data={"combined": {"mlag_peer_ipv4_pool": self._mlag_peer_ipv4_pool}},
        )

    return self._mlag_ip(self._mlag_peer_ipv4_pool, 0)

pyavd.api.ip_addressing.AvdIpAddressing.mlag_ip_secondary()

Return IP for MLAG Secondary.

Default pool is “mlag_peer_ipv4_pool”

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def mlag_ip_secondary(self) -> str:
    """
    Return IP for MLAG Secondary.

    Default pool is "mlag_peer_ipv4_pool"
    """
    if self.shared_utils.mlag_peer_address_family == "ipv6":
        if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ip_secondary"):
            return self._template(
                template_path,
                mlag_primary_id=self._mlag_primary_id,
                mlag_secondary_id=self._mlag_secondary_id,
                switch_data={"combined": {"mlag_peer_ipv6_pool": self._mlag_peer_ipv6_pool}},
            )

        return self._mlag_ip(self._mlag_peer_ipv6_pool, 1, self.shared_utils.mlag_peer_address_family)

    if template_path := self.shared_utils.ip_addressing_templates.get("mlag_ip_secondary"):
        return self._template(
            template_path,
            mlag_primary_id=self._mlag_primary_id,
            mlag_secondary_id=self._mlag_secondary_id,
            switch_data={"combined": {"mlag_peer_ipv4_pool": self._mlag_peer_ipv4_pool}},
        )

    return self._mlag_ip(self._mlag_peer_ipv4_pool, 1)

pyavd.api.ip_addressing.AvdIpAddressing.mlag_l3_ip_primary()

Return IP for L3 Peerings for MLAG Primary.

Default pool is “mlag_peer_l3_ipv4_pool”

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def mlag_l3_ip_primary(self) -> str:
    """
    Return IP for L3 Peerings for MLAG Primary.

    Default pool is "mlag_peer_l3_ipv4_pool"
    """
    if template_path := self.shared_utils.ip_addressing_templates.get("mlag_l3_ip_primary"):
        return self._template(
            template_path,
            mlag_primary_id=self._mlag_primary_id,
            mlag_secondary_id=self._mlag_secondary_id,
            switch_data={"combined": {"mlag_peer_l3_ipv4_pool": self._mlag_peer_l3_ipv4_pool}},
        )

    return self._mlag_ip(self._mlag_peer_l3_ipv4_pool, 0)

pyavd.api.ip_addressing.AvdIpAddressing.mlag_l3_ip_secondary()

Return IP for L3 Peerings for MLAG Secondary.

Default pool is “mlag_peer_l3_ipv4_pool”

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def mlag_l3_ip_secondary(self) -> str:
    """
    Return IP for L3 Peerings for MLAG Secondary.

    Default pool is "mlag_peer_l3_ipv4_pool"
    """
    if template_path := self.shared_utils.ip_addressing_templates.get("mlag_l3_ip_secondary"):
        return self._template(
            template_path,
            mlag_primary_id=self._mlag_primary_id,
            mlag_secondary_id=self._mlag_secondary_id,
            switch_data={"combined": {"mlag_peer_l3_ipv4_pool": self._mlag_peer_l3_ipv4_pool}},
        )

    return self._mlag_ip(self._mlag_peer_l3_ipv4_pool, 1)

Return Child IP for P2P Uplinks.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def p2p_uplinks_ip(self, uplink_switch_index: int) -> str:
    """Return Child IP for P2P Uplinks."""
    uplink_switch_index = int(uplink_switch_index)
    if template_path := self.shared_utils.ip_addressing_templates.get("p2p_uplinks_ip"):
        return self._template(
            template_path,
            uplink_switch_index=uplink_switch_index,
        )

    prefixlen = self._fabric_ip_addressing_p2p_uplinks_ipv4_prefix_length
    p2p_ipv4_pool, offset = self._get_p2p_ipv4_pool_and_offset(uplink_switch_index)

    return get_ip_from_pool(p2p_ipv4_pool, prefixlen, offset, 1)

Return Parent IP for P2P Uplinks.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def p2p_uplinks_peer_ip(self, uplink_switch_index: int) -> str:
    """Return Parent IP for P2P Uplinks."""
    uplink_switch_index = int(uplink_switch_index)
    if template_path := self.shared_utils.ip_addressing_templates.get("p2p_uplinks_peer_ip"):
        return self._template(
            template_path,
            uplink_switch_index=uplink_switch_index,
        )

    prefixlen = self._fabric_ip_addressing_p2p_uplinks_ipv4_prefix_length
    p2p_ipv4_pool, offset = self._get_p2p_ipv4_pool_and_offset(uplink_switch_index)

    return get_ip_from_pool(p2p_ipv4_pool, prefixlen, offset, 0)

Return Child IP for P2P-VRFs Uplinks.

Unless overridden in a custom IP addressing module, this will just reuse the regular ip addressing logic.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def p2p_vrfs_uplinks_ip(
    self,
    uplink_switch_index: int,
    vrf: str,  # pylint: disable=unused-argument # NOSONAR # noqa: ARG002
) -> str:
    """
    Return Child IP for P2P-VRFs Uplinks.

    Unless overridden in a custom IP addressing module, this will just reuse the regular ip addressing logic.
    """
    return self.p2p_uplinks_ip(uplink_switch_index)

Return Parent IP for P2P-VRFs Uplinks.

Unless overridden in a custom IP addressing module, this will just reuse the regular ip addressing logic.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def p2p_vrfs_uplinks_peer_ip(
    self,
    uplink_switch_index: int,
    vrf: str,  # pylint: disable=unused-argument # NOSONAR # noqa: ARG002
) -> str:
    """
    Return Parent IP for P2P-VRFs Uplinks.

    Unless overridden in a custom IP addressing module, this will just reuse the regular ip addressing logic.
    """
    return self.p2p_uplinks_peer_ip(uplink_switch_index)

pyavd.api.ip_addressing.AvdIpAddressing.router_id()

Return IP address for Router ID.

If “loopback_ipv4_address” is set, it is used. Default pool is “loopback_ipv4_pool” Default offset from pool is id + loopback_ipv4_offset

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def router_id(self) -> str:
    """
    Return IP address for Router ID.

    If "loopback_ipv4_address" is set, it is used.
    Default pool is "loopback_ipv4_pool"
    Default offset from pool is `id + loopback_ipv4_offset`
    """
    if self._loopback_ipv4_address:
        return self._loopback_ipv4_address

    if template_path := self.shared_utils.ip_addressing_templates.get("router_id"):
        return self._template(
            template_path,
            switch_id=self._id,
            loopback_ipv4_pool=self._loopback_ipv4_pool,
            loopback_ipv4_offset=self._loopback_ipv4_offset,
        )

    offset = self._id + self._loopback_ipv4_offset
    return get_ip_from_pool(self._loopback_ipv4_pool, 32, offset, 0)

pyavd.api.ip_addressing.AvdIpAddressing.vrf_loopback_ip(pool)

Return IP address for a Loopback interface based on the given pool.

Default offset from pool is id + loopback_ipv4_offset.

Used for “vtep_diagnostic.loopback”.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def vrf_loopback_ip(self, pool: str) -> str:
    """
    Return IP address for a Loopback interface based on the given pool.

    Default offset from pool is `id + loopback_ipv4_offset`.

    Used for "vtep_diagnostic.loopback".
    """
    offset = self.shared_utils.id + self.shared_utils.loopback_ipv4_offset
    return get_ip_from_pool(pool, 32, offset, 0)

pyavd.api.ip_addressing.AvdIpAddressing.vrf_loopback_ipv6(pool)

Return IPv6 address for a Loopback interface based on the given pool.

Default offset from pool is id + loopback_ipv6_offset.

Used for “vtep_diagnostic.loopback”.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def vrf_loopback_ipv6(self, pool: str) -> str:
    """
    Return IPv6 address for a Loopback interface based on the given pool.

    Default offset from pool is `id + loopback_ipv6_offset`.

    Used for "vtep_diagnostic.loopback".
    """
    offset = self.shared_utils.id + self.shared_utils.loopback_ipv6_offset
    return get_ip_from_pool(pool, 128, offset, 0)

pyavd.api.ip_addressing.AvdIpAddressing.vtep_ip()

Return IP address for VTEP.

If “vtep_loopback_ipv4_address” is set, it is used. Default pool is “vtep_loopback_ipv4_pool” Default offset from pool is id + loopback_ipv4_offset

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def vtep_ip(self) -> str:
    """
    Return IP address for VTEP.

    If "vtep_loopback_ipv4_address" is set, it is used.
    Default pool is "vtep_loopback_ipv4_pool"
    Default offset from pool is `id + loopback_ipv4_offset`
    """
    if self._vtep_loopback_ipv4_address:
        return self._vtep_loopback_ipv4_address

    if template_path := self.shared_utils.ip_addressing_templates.get("vtep_ip"):
        return self._template(
            template_path,
            switch_id=self._id,
            switch_vtep_loopback_ipv4_pool=self._vtep_loopback_ipv4_pool,
            loopback_ipv4_offset=self._loopback_ipv4_offset,
        )

    offset = self._id + self._loopback_ipv4_offset
    return get_ip_from_pool(self._vtep_loopback_ipv4_pool, 32, offset, 0)

pyavd.api.ip_addressing.AvdIpAddressing.vtep_ip_mlag()

Return IP address for VTEP for MLAG Leaf.

If “vtep_loopback_ipv4_address” is set, it is used. Default pool is “vtep_loopback_ipv4_pool” Default offset from pool is mlag_primary_id + loopback_ipv4_offset

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def vtep_ip_mlag(self) -> str:
    """
    Return IP address for VTEP for MLAG Leaf.

    If "vtep_loopback_ipv4_address" is set, it is used.
    Default pool is "vtep_loopback_ipv4_pool"
    Default offset from pool is `mlag_primary_id + loopback_ipv4_offset`
    """
    if self._vtep_loopback_ipv4_address:
        return self._vtep_loopback_ipv4_address

    if template_path := self.shared_utils.ip_addressing_templates.get("vtep_ip_mlag"):
        return self._template(
            template_path,
            switch_id=self._id,
            switch_vtep_loopback_ipv4_pool=self._vtep_loopback_ipv4_pool,
            loopback_ipv4_offset=self._loopback_ipv4_offset,
            mlag_primary_id=self._mlag_primary_id,
            mlag_secondary_id=self._mlag_secondary_id,
        )

    offset = self._mlag_primary_id + self._loopback_ipv4_offset
    return get_ip_from_pool(self._vtep_loopback_ipv4_pool, 32, offset, 0)

pyavd.api.ip_addressing.AvdIpAddressing.wan_ha_ip()

Return the WAN HA local IP address.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def wan_ha_ip(self) -> str:
    """Return the WAN HA local IP address."""
    wan_ha_ipv4_pool = self.shared_utils.wan_ha_ipv4_pool
    prefixlen = self.shared_utils.fabric_ip_addressing_wan_ha_ipv4_prefix_length

    if self.shared_utils.is_first_ha_peer:
        ip_address = get_ip_from_pool(wan_ha_ipv4_pool, prefixlen, 0, 0)
    else:
        ip_address = get_ip_from_pool(wan_ha_ipv4_pool, prefixlen, 0, 1)

    return f"{ip_address}/{prefixlen}"

pyavd.api.ip_addressing.AvdIpAddressing.wan_ha_peer_ip()

Return the WAN HA peer IP.

Source code in python-avd/pyavd/api/ip_addressing/__init__.py
def wan_ha_peer_ip(self) -> str:
    """Return the WAN HA peer IP."""
    wan_ha_ipv4_pool = self.shared_utils.wan_ha_ipv4_pool
    prefixlen = self.shared_utils.fabric_ip_addressing_wan_ha_ipv4_prefix_length

    if self.shared_utils.is_first_ha_peer:
        ip_address = get_ip_from_pool(wan_ha_ipv4_pool, prefixlen, 0, 1)
    else:
        ip_address = get_ip_from_pool(wan_ha_ipv4_pool, prefixlen, 0, 0)

    return f"{ip_address}/{prefixlen}"