|
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)
|