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>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
b7e4831b26
commit
2c0a5e94bf
@@ -0,0 +1,101 @@
|
||||
# -*- 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
|
||||
Reference in New Issue
Block a user