From 9821aa7a47fddb2f9e9d36bfffc1d55a17682667 Mon Sep 17 00:00:00 2001
From: Daniil Baturin <daniil@baturin.org>
Date: Thu, 3 Oct 2024 16:43:39 +0100
Subject: cli: T6740: add a converter from set commands to config

---
 python/vyos/utils/config.py       | 41 +++++++++++++++++++++++++++++++++++++++
 python/vyos/xml_ref/definition.py | 28 +++++++++++++++++++++++++-
 2 files changed, 68 insertions(+), 1 deletion(-)

(limited to 'python')

diff --git a/python/vyos/utils/config.py b/python/vyos/utils/config.py
index 33047010b..b02621e7b 100644
--- a/python/vyos/utils/config.py
+++ b/python/vyos/utils/config.py
@@ -37,3 +37,44 @@ def read_saved_value(path: list):
     if len(res) == 1:
         return ' '.join(res)
     return res
+
+def parse_commands(cmds: str) -> dict:
+    from re import split as re_split
+    from shlex import split as shlex_split
+
+    from vyos.xml_ref import definition
+    from vyos.xml_ref.pkg_cache.vyos_1x_cache import reference
+
+    ref_tree = definition.Xml()
+    ref_tree.define(reference)
+
+    res = []
+
+    cmds = re_split(r'\n+', cmds)
+    for c in cmds:
+        cmd_parts = shlex_split(c)
+
+        if not cmd_parts:
+            # Ignore empty lines
+            continue
+
+        path = cmd_parts[1:]
+        op = cmd_parts[0]
+
+        try:
+            path, value = ref_tree.split_path(path)
+        except ValueError as e:
+            raise ValueError(f'Incorrect command: {e}')
+
+        entry = {}
+        entry["op"] = op
+        entry["path"] = path
+        entry["value"] = value
+
+        entry["is_multi"] = ref_tree.is_multi(path)
+        entry["is_leaf"] = ref_tree.is_leaf(path)
+        entry["is_tag"] = ref_tree.is_tag(path)
+
+        res.append(entry)
+
+    return res
diff --git a/python/vyos/xml_ref/definition.py b/python/vyos/xml_ref/definition.py
index 5ff28daed..4e755ab72 100644
--- a/python/vyos/xml_ref/definition.py
+++ b/python/vyos/xml_ref/definition.py
@@ -13,7 +13,7 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
-from typing import Optional, Union, Any, TYPE_CHECKING
+from typing import Tuple, Optional, Union, Any, TYPE_CHECKING
 
 # https://peps.python.org/pep-0484/#forward-references
 # for type 'ConfigDict'
@@ -90,6 +90,32 @@ class Xml:
         res = self._get_ref_node_data(node, 'node_type')
         return res == 'tag'
 
+    def exists(self, path: list) -> bool:
+        try:
+            _ = self._get_ref_path(path)
+            return True
+        except ValueError:
+            return False
+
+    def split_path(self, path: list) -> Tuple[list, Optional[str]]:
+        """ Splits a list into config path and value components """
+
+        # First, check if the complete path is valid by itself
+        if self.exists(path):
+            if self.is_valueless(path) or not self.is_leaf(path):
+                # It's a complete path for a valueless node
+                # or a path to an empy non-leaf node
+                return (path, None)
+            else:
+                raise ValueError(f'Path "{path}" needs a value or children')
+        else:
+            # If the complete path doesn't exist, it's probably a path with a value
+            if self.exists(path[0:-1]):
+                return (path[0:-1], path[-1])
+            else:
+                # Or not a valid path at all
+                raise ValueError(f'Path "{path}" is incorrect')
+
     def is_tag(self, path: list) -> bool:
         ref_path = path.copy()
         d = self.ref
-- 
cgit v1.2.3


From 8013e228320a414087e3456c1632b479483b856c Mon Sep 17 00:00:00 2001
From: John Estabrook <jestabro@vyos.io>
Date: Thu, 3 Oct 2024 15:27:27 -0500
Subject: cli: T6740: set_tag on created paths and add parse step for ordering

Signed-off-by: Daniil Baturin <daniil@baturin.org>
---
 python/vyos/utils/config.py       | 25 +++++++++++++++++++++++++
 src/utils/vyos-commands-to-config |  7 +++++--
 2 files changed, 30 insertions(+), 2 deletions(-)

(limited to 'python')

diff --git a/python/vyos/utils/config.py b/python/vyos/utils/config.py
index b02621e7b..deda13c13 100644
--- a/python/vyos/utils/config.py
+++ b/python/vyos/utils/config.py
@@ -14,8 +14,14 @@
 # License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
+from typing import TYPE_CHECKING
+
 from vyos.defaults import directories
 
+# https://peps.python.org/pep-0484/#forward-references
+if TYPE_CHECKING:
+    from vyos.configtree import ConfigTree
+
 config_file = os.path.join(directories['config'], 'config.boot')
 
 def read_saved_value(path: list):
@@ -38,6 +44,25 @@ def read_saved_value(path: list):
         return ' '.join(res)
     return res
 
+def flag(l: list) -> list:
+    res = [l[0:i] for i,_ in enumerate(l, start=1)]
+    return res
+
+def tag_node_of_path(p: list) -> list:
+    from vyos.xml_ref import is_tag
+
+    fl = flag(p)
+    res = list(map(is_tag, fl))
+
+    return res
+
+def set_tags(ct: 'ConfigTree', path: list) -> None:
+    fl = flag(path)
+    if_tag = tag_node_of_path(path)
+    for condition, target in zip(if_tag, fl):
+        if condition:
+            ct.set_tag(target)
+
 def parse_commands(cmds: str) -> dict:
     from re import split as re_split
     from shlex import split as shlex_split
diff --git a/src/utils/vyos-commands-to-config b/src/utils/vyos-commands-to-config
index 85613b49d..927d9bd70 100755
--- a/src/utils/vyos-commands-to-config
+++ b/src/utils/vyos-commands-to-config
@@ -20,7 +20,7 @@ import json
 
 from vyos.configtree import ConfigTree
 from vyos.utils.config import parse_commands
-
+from vyos.utils.config import set_tags
 
 def commands_to_config(cmds):
     ct = ConfigTree('')
@@ -31,8 +31,10 @@ def commands_to_config(cmds):
             if c["is_leaf"]:
                 replace = False if c["is_multi"] else True
                 ct.set(c["path"], value=c["value"], replace=replace)
+                set_tags(ct, c["path"])
             else:
                 ct.create_node(c["path"])
+                set_tags(ct, c["path"])
         else:
             raise ValueError(
               f"\"{c['op']}\" is not a supported config operation")
@@ -44,7 +46,8 @@ if __name__ == '__main__':
     try:
         cmds = sys.stdin.read()
         ct = commands_to_config(cmds)
-        print(str(ct))
+        out = ConfigTree(ct.to_string())
+        print(str(out))
     except Exception as e:
         print(e)
         sys.exit(1)
-- 
cgit v1.2.3