Blame libnmstate/state.py

Packit b9ca78
#
Packit b9ca78
# Copyright (c) 2020 Red Hat, Inc.
Packit b9ca78
#
Packit b9ca78
# This file is part of nmstate
Packit b9ca78
#
Packit b9ca78
# This program is free software: you can redistribute it and/or modify
Packit b9ca78
# it under the terms of the GNU Lesser General Public License as published by
Packit b9ca78
# the Free Software Foundation, either version 2.1 of the License, or
Packit b9ca78
# (at your option) any later version.
Packit b9ca78
#
Packit b9ca78
# This program is distributed in the hope that it will be useful,
Packit b9ca78
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b9ca78
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit b9ca78
# GNU Lesser General Public License for more details.
Packit b9ca78
#
Packit b9ca78
# You should have received a copy of the GNU Lesser General Public License
Packit b9ca78
# along with this program. If not, see <https://www.gnu.org/licenses/>.
Packit b9ca78
#
Packit b9ca78
Packit b9ca78
from abc import ABCMeta
Packit b9ca78
from abc import abstractmethod
Packit b9ca78
from collections.abc import Sequence
Packit b9ca78
from collections.abc import Mapping
Packit b9ca78
from functools import total_ordering
Packit b9ca78
Packit b9ca78
Packit b9ca78
@total_ordering
Packit b9ca78
class StateEntry(metaclass=ABCMeta):
Packit b9ca78
    @abstractmethod
Packit b9ca78
    def _keys(self):
Packit b9ca78
        """
Packit b9ca78
        Return the tuple representing this entry, will be used for hashing or
Packit b9ca78
        comparing.
Packit b9ca78
        """
Packit b9ca78
        pass
Packit b9ca78
Packit b9ca78
    def __hash__(self):
Packit b9ca78
        return hash(self._keys())
Packit b9ca78
Packit b9ca78
    def __eq__(self, other):
Packit b9ca78
        return self is other or self._keys() == other._keys()
Packit b9ca78
Packit b9ca78
    def __lt__(self, other):
Packit b9ca78
        return self._keys() < other._keys()
Packit b9ca78
Packit b9ca78
    def __repr__(self):
Packit b9ca78
        return str(self.to_dict())
Packit b9ca78
Packit b9ca78
    @property
Packit b9ca78
    @abstractmethod
Packit b9ca78
    def absent(self):
Packit b9ca78
        pass
Packit b9ca78
Packit b9ca78
    def to_dict(self):
Packit b9ca78
        return {
Packit b9ca78
            key.replace("_", "-"): value
Packit b9ca78
            for key, value in vars(self).items()
Packit b9ca78
            if (not key.startswith("_")) and (value is not None)
Packit b9ca78
        }
Packit b9ca78
Packit b9ca78
    def match(self, other):
Packit b9ca78
        """
Packit b9ca78
        Match self against other. Treat self None attributes as wildcards,
Packit b9ca78
        matching against any value in others.
Packit b9ca78
        Return True for a match, False otherwise.
Packit b9ca78
        """
Packit b9ca78
        for self_value, other_value in zip(self._keys(), other._keys()):
Packit b9ca78
            if self_value is not None and self_value != other_value:
Packit b9ca78
                return False
Packit b9ca78
        return True
Packit b9ca78
Packit b9ca78
Packit b9ca78
def state_match(desire, current):
Packit b9ca78
    """
Packit b9ca78
    Return True when all values defined in desire equal to value in current,
Packit b9ca78
    else False:
Packit b9ca78
        * For mapping(e.g. dict), desire could have less value than current.
Packit b9ca78
        * For sequnce(e.g. list), desire should equal to current.
Packit b9ca78
    """
Packit b9ca78
    if isinstance(desire, Mapping):
Packit b9ca78
        return isinstance(current, Mapping) and all(
Packit b9ca78
            state_match(val, current.get(key)) for key, val in desire.items()
Packit b9ca78
        )
Packit b9ca78
    elif isinstance(desire, Sequence) and not isinstance(desire, str):
Packit b9ca78
        return (
Packit b9ca78
            isinstance(current, Sequence)
Packit b9ca78
            and not isinstance(current, str)
Packit b9ca78
            and len(current) == len(desire)
Packit b9ca78
            and all(state_match(d, c) for d, c in zip(desire, current))
Packit b9ca78
        )
Packit b9ca78
    else:
Packit b9ca78
        return desire == current
Packit b9ca78
Packit b9ca78
Packit b9ca78
def merge_dict(dict_to, dict_from):
Packit b9ca78
    """
Packit b9ca78
    Data will copy from `dict_from` if undefined in `dict_to`.
Packit b9ca78
    For list, the whole list is copied instead of merging.
Packit b9ca78
    """
Packit b9ca78
    for key, from_value in dict_from.items():
Packit b9ca78
        if key not in dict_to:
Packit b9ca78
            dict_to[key] = from_value
Packit b9ca78
        elif isinstance(dict_to[key], Mapping):
Packit b9ca78
            merge_dict(dict_to[key], from_value)