Files
napalm-openwrt/napalm_openwrt/config_mixin.py
T
Christian ManivongandClaude Sonnet 4.6 2c0a5e94bf chore: track extracted mixin modules
Split from monolithic openwrt.py into separate mixin files for
interfaces, VLANs, packages, routing, wireless, LLDP, and config.
Already in use by the main driver; just missing from git tracking.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:13:04 +02:00

102 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
from napalm.base.exceptions import (
CommandErrorException,
MergeConfigException,
ReplaceConfigException,
)
from napalm_device_types import ConfigLifecycleMixin
class OpenWrtConfigMixin(ConfigLifecycleMixin):
"""Mixin providing configuration-management NAPALM methods for OpenWrt.
Reuses the standard lifecycle (load/compare/discard) from
:class:`ConfigLifecycleMixin` and adds OpenWrt-specific
``commit_config`` and ``rollback``.
"""
_comment_chars = ("#",)
def _get_running_config(self) -> str:
return self._send_command("uci export")
def commit_config(self, message: str = "", revert_in: int | None = None) -> None:
"""Apply the staged candidate configuration and commit it.
**Merge mode**: each UCI command line is sent to the device shell, then
``uci commit`` is called to persist the changes.
**Replace mode**: the candidate is piped through ``uci import`` and
then ``uci commit`` is called for every affected package.
:raises MergeConfigException: if no candidate is staged or if
commands are rejected.
:raises ReplaceConfigException: same, for replace candidates.
"""
if self._candidate_config is None:
raise MergeConfigException("No candidate configuration is staged.")
ex_cls = ReplaceConfigException if self._candidate_mode == "replace" else MergeConfigException
self._backup_config = self._send_command("uci export")
errors: list[str] = []
try:
if self._candidate_mode == "merge":
for line in self._candidate_config.splitlines():
stripped = line.strip()
if not stripped or stripped.startswith("#"):
continue
out = self._send_command(stripped)
if out and ("uci: " in out.lower() or "error" in out.lower()):
errors.append(f" {stripped!r}: {out}")
self._send_command("uci commit")
else:
escaped = self._candidate_config.replace("'", "'\\''")
self._send_command(f"printf '%s' '{escaped}' > /tmp/napalm_candidate.uci")
out = self._send_command("uci import < /tmp/napalm_candidate.uci && uci commit")
self._send_command("rm -f /tmp/napalm_candidate.uci")
if out and "error" in out.lower():
errors.append(out)
except Exception as exc:
raise ex_cls(str(exc)) from exc
if errors:
raise ex_cls("The following commands were rejected:\n" + "\n".join(errors))
self._candidate_config = None
self._candidate_mode = None
def rollback(self) -> None:
"""Restore the UCI configuration to the state before the last :meth:`commit_config`.
Pipes the saved backup through ``uci import`` and then commits.
:raises CommandErrorException: if no backup is available.
"""
if self._backup_config is None:
raise CommandErrorException(
"No backup configuration available – commit_config has not been called in this session."
)
escaped = self._backup_config.replace("'", "'\\''")
self._send_command(f"printf '%s' '{escaped}' > /tmp/napalm_rollback.uci")
self._send_command("uci import < /tmp/napalm_rollback.uci && uci commit")
self._send_command("rm -f /tmp/napalm_rollback.uci")
self._backup_config = None