diff --git a/.gitignore b/.gitignore index 875fb89..e8d44e6 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,6 @@ site/ # direnv .envrc + +# Brainstorming / design specs (local only, not for version control) +docs/superpowers/ diff --git a/docs/configuration.md b/docs/configuration.md index 015d9de..205828d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -49,21 +49,6 @@ NetBox installs: interface name = "5" (raw bay position) Plugin renames: interface name = "et-0/0/5" ``` -### `{module_path}` (NetBox ≥ 4.9) - -When a module type's interface template uses `{module_path}`, NetBox resolves -the full module bay path at install time. For a transceiver in "X2 Port 1" -inside a line card in "Slot 1", `{module_path}` resolves to `1/1`. The plugin -signal still fires, but may compute the same name the interface already has — -so it becomes a no-op. - -For directly-attached pluggables (single bay depth), `{module_path}` resolves to -just the bay position, behaving identically to `{module}`. - -!!! tip - New module types should use `{module_path}` for best NetBox compatibility. - Legacy module types using `{module}` continue to work — the plugin handles the rename. - ### The `potentially-deprecated` Tag After installing a module, if the plugin's signal fires but finds the interface @@ -71,8 +56,7 @@ is already correctly named, it automatically tags the rule `potentially-deprecat This means: - For **new installs**: the rule may no longer be needed (NetBox generates the name) -- For **retroactive applies**: the rule is still useful for modules installed before - `{module_path}` support was added, or before the rule existed +- For **retroactive applies**: the rule is still useful for modules installed before the rule existed The tag is informational only — the rule remains active. diff --git a/netbox_interface_name_rules/engine.py b/netbox_interface_name_rules/engine.py index c1c2ecb..64ae903 100644 --- a/netbox_interface_name_rules/engine.py +++ b/netbox_interface_name_rules/engine.py @@ -561,8 +561,7 @@ def has_applicable_interfaces(rule) -> bool: Calls find_interfaces_for_rule(limit=1) to determine if any currently installed interface would receive a new name. Returns False when: - no matching modules/interfaces are installed, OR - - all matching interfaces are already correctly named (e.g. NetBox resolved - {module_path} at install time, making the rule a no-op for existing interfaces). + - all matching interfaces are already correctly named. This is more expensive than a plain EXISTS query but ensures the Applicable column in the Apply Rules list accurately reflects "would something change?" diff --git a/netbox_interface_name_rules/models.py b/netbox_interface_name_rules/models.py index 314b165..1d14891 100644 --- a/netbox_interface_name_rules/models.py +++ b/netbox_interface_name_rules/models.py @@ -283,3 +283,45 @@ def __str__(self): device = f" on {self.device_type.model}" if self.device_type else "" platform = f" [{self.platform.name}]" if self.platform else "" return f"{module}{parent}{device}{platform} → {self.name_template}" + + csv_headers = [ + "module_type", + "module_type_pattern", + "module_type_is_regex", + "parent_module_type", + "device_type", + "platform", + "name_template", + "channel_count", + "channel_start", + "description", + "enabled", + "applies_to_device_interfaces", + ] + + def to_csv(self): + """Return a tuple of field values for CSV export (matches csv_headers order).""" + return ( + self.module_type.model if self.module_type else "", + self.module_type_pattern, + self.module_type_is_regex, + self.parent_module_type.model if self.parent_module_type else "", + self.device_type.model if self.device_type else "", + self.platform.name if self.platform else "", + self.name_template, + self.channel_count, + self.channel_start, + self.description, + self.enabled, + self.applies_to_device_interfaces, + ) + + def to_yaml(self): + """Return a YAML document for this rule (used by NetBox's built-in Export).""" + import yaml + + entry = {} + for header, value in zip(self.csv_headers, self.to_csv()): + if (value != "" and value is not None) or header in {"name_template"}: + entry[header] = value + return yaml.dump([entry], default_flow_style=False, allow_unicode=True, sort_keys=False) diff --git a/netbox_interface_name_rules/tables.py b/netbox_interface_name_rules/tables.py index 5d38970..89e3ca0 100644 --- a/netbox_interface_name_rules/tables.py +++ b/netbox_interface_name_rules/tables.py @@ -96,7 +96,7 @@ class InterfaceNameRuleTable(NetBoxTable): applies_to_device_interfaces = columns.BooleanColumn(verbose_name="Device Ifaces") name_template = tables.Column(verbose_name="Name Template") channel_count = tables.Column(verbose_name="Channels") - channel_start = tables.Column(verbose_name="Ch. Start") + channel_start = tables.Column(verbose_name="Channel Start") description = tables.Column(verbose_name="Description", linkify=False) actions = columns.ActionsColumn( actions=("edit", "delete"), diff --git a/netbox_interface_name_rules/templates/netbox_interface_name_rules/buttons/export_yaml_only.html b/netbox_interface_name_rules/templates/netbox_interface_name_rules/buttons/export_yaml_only.html new file mode 100644 index 0000000..d1ccffc --- /dev/null +++ b/netbox_interface_name_rules/templates/netbox_interface_name_rules/buttons/export_yaml_only.html @@ -0,0 +1,33 @@ +{# SPDX-License-Identifier: Apache-2.0 #} +{# Copyright (C) 2025 Marcin Zieba #} +{% load i18n %} + diff --git a/netbox_interface_name_rules/templates/netbox_interface_name_rules/interfacenamerule_list.html b/netbox_interface_name_rules/templates/netbox_interface_name_rules/interfacenamerule_list.html index 9ca0f97..217b0d1 100644 --- a/netbox_interface_name_rules/templates/netbox_interface_name_rules/interfacenamerule_list.html +++ b/netbox_interface_name_rules/templates/netbox_interface_name_rules/interfacenamerule_list.html @@ -78,23 +78,6 @@
Examples
(device-level rule, pattern Ethernet\d+/\d+) - {% if supports_module_path %} -
- - {module_path} supported: - Platform naming rules (e.g., et-0/0/{bay_position}) - may be replaceable by using {module_path} in module type interface templates directly. - Converter offset and breakout rules are still needed. -
- {% else %} -
- - {module_path} not available: - Platform naming rules - (e.g., Juniper et-0/0/{bay_position}) are required for correct interface naming. - Upgrade NetBox to get native {module_path} support. -
- {% endif %}