Blob Blame History Raw
From d0bfae4171f0d280241949a928654c84e63ed006 Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Mon, 29 Jun 2020 22:30:03 +0800
Subject: [PATCH] pretty state: Dumping state in the sorted order with priority

For OVS bond port, we would like the `name` been shown before other
properties.

To achieve that, we sort the dict keys and honoring the priority list which
means keys in `PRIORITY_LIST` will be shown before others.

Currently the `PRIORITY_LIST` is:
`("name", "type", "state", "enabled", "dns", "route-rules", "routes",
  "interfaces")`

Test cases added.

Signed-off-by: Gris Ge <fge@redhat.com>
Signed-off-by: Till Maas <till@redhat.com>
---
 libnmstate/prettystate.py | 94 ++++++++++++++++++---------------------
 1 file changed, 43 insertions(+), 51 deletions(-)

diff --git a/libnmstate/prettystate.py b/libnmstate/prettystate.py
index da57618..10e22d6 100644
--- a/libnmstate/prettystate.py
+++ b/libnmstate/prettystate.py
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2018-2019 Red Hat, Inc.
+# Copyright (c) 2018-2020 Red Hat, Inc.
 #
 # This file is part of nmstate
 #
@@ -17,15 +17,29 @@
 # along with this program. If not, see <https://www.gnu.org/licenses/>.
 #
 
-from collections import OrderedDict
+from collections.abc import Mapping
+from collections.abc import Sequence
 from copy import deepcopy
 import difflib
 import json
-from operator import itemgetter
 
 import yaml
 
-from libnmstate.schema import Constants
+from .schema import DNS
+from .schema import Route
+from .schema import RouteRule
+from .schema import Interface
+
+PRIORITY_LIST = (
+    "name",
+    "type",
+    "state",
+    "enabled",
+    DNS.KEY,
+    RouteRule.KEY,
+    Route.KEY,
+    Interface.KEY,
+)
 
 
 def format_desired_current_state_diff(desired_state, current_state):
@@ -57,8 +71,8 @@ def format_desired_current_state_diff(desired_state, current_state):
 
 class PrettyState:
     def __init__(self, state):
-        yaml.add_representer(OrderedDict, represent_ordereddict)
-        self.state = order_state(deepcopy(state))
+        yaml.add_representer(dict, represent_dict)
+        self.state = _sort_with_priority(state)
 
     @property
     def yaml(self):
@@ -71,35 +85,18 @@ class PrettyState:
         return json.dumps(self.state, indent=4, separators=(",", ": "))
 
 
-def represent_ordereddict(dumper, data):
+def represent_dict(dumper, data):
+    """
+    Represent dictionary with insert order
     """
-    Represent OrderedDict as regular dictionary
-
-    Source: https://stackoverflow.com/questions/16782112/can-pyyaml-dump-dict-items-in-non-alphabetical-order
-    """  # noqa: E501
     value = []
 
     for item_key, item_value in data.items():
         node_key = dumper.represent_data(item_key)
         node_value = dumper.represent_data(item_value)
-
         value.append((node_key, node_value))
 
-    return yaml.nodes.MappingNode(u"tag:yaml.org,2002:map", value)
-
-
-def order_state(state):
-    iface_states = state.pop(Constants.INTERFACES, None)
-
-    state = order_iface_state(state)
-
-    if iface_states is not None:
-        state[Constants.INTERFACES] = [
-            order_iface_state(iface_state)
-            for iface_state in sorted(iface_states, key=itemgetter("name"))
-        ]
-
-    return state
+    return yaml.nodes.MappingNode("tag:yaml.org,2002:map", value)
 
 
 def represent_unicode(_, data):
@@ -112,30 +109,25 @@ def represent_unicode(_, data):
     """
 
     return yaml.ScalarNode(
-        tag=u"tag:yaml.org,2002:str", value=data.encode("utf-8")
+        tag="tag:yaml.org,2002:str", value=data.encode("utf-8")
     )
 
 
-def order_iface_state(iface_state):
-    ordered_state = OrderedDict()
-
-    for setting in ("name", "type", "state"):
-        try:
-            ordered_state[setting] = iface_state.pop(setting)
-        except KeyError:
-            pass
-
-    for key, value in order_dict(iface_state).items():
-        ordered_state[key] = value
-
-    return ordered_state
-
-
-def order_dict(dict_):
-    ordered_dict = OrderedDict()
-    for key, value in sorted(dict_.items()):
-        if isinstance(value, dict):
-            value = order_dict(value)
-        ordered_dict[key] = value
-
-    return ordered_dict
+def _sort_with_priority(data):
+    if isinstance(data, Sequence) and not isinstance(data, str):
+        return [_sort_with_priority(item) for item in data]
+    elif isinstance(data, Mapping):
+        new_data = {}
+        for key in sorted(data.keys(), key=_sort_with_priority_key_func):
+            new_data[key] = _sort_with_priority(data[key])
+        return new_data
+    else:
+        return deepcopy(data)
+
+
+def _sort_with_priority_key_func(key):
+    try:
+        priority = PRIORITY_LIST.index(key)
+    except ValueError:
+        priority = len(PRIORITY_LIST)
+    return (priority, key)
-- 
2.27.0