diff --git a/schema/op-mode-definition.rnc b/schema/op-mode-definition.rnc
new file mode 100644
index 000000000..01276123c
--- /dev/null
+++ b/schema/op-mode-definition.rnc
@@ -0,0 +1,108 @@
+#    interface_definition.rnc: VyConf reference tree XML grammar
+#
+#    Copyright (C) 2014. 2017 VyOS maintainers and contributors <maintainers@vyos.net>
+#
+#    This library is free software; you can redistribute it and/or
+#    modify it under the terms of the GNU Lesser General Public
+#    License as published by the Free Software Foundation; either
+#    version 2.1 of the License, or (at your option) any later version.
+#
+#    This library is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#    Lesser General Public License for more details.
+#
+#    You should have received a copy of the GNU Lesser General Public
+#    License along with this library; if not, write to the Free Software
+#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+#    USA
+
+# The language of this file is compact form RELAX-NG
+# http://relaxng.org/compact-tutorial-20030326.htm
+# (unless converted to XML, then just RELAX-NG :)
+
+# Interface definition starts with interfaceDefinition tag that may contain node tags
+start = element interfaceDefinition
+{
+    node*
+}
+
+# node tag may contain node, leafNode, or tagNode tags
+# Those are intermediate configuration nodes that may only contain
+# other nodes and must not have values
+node = element node
+{
+    nodeNameAttr,
+    (properties? & children? & command?)
+}
+
+# Tag nodes are containers for nodes without predefined names, like network interfaces
+# or user names (e.g. "interfaces ethernet eth0" or "user jrandomhacker")
+# Tag nodes may contain node and leafNode elements, and also nameConstraint tags
+# They must not contain other tag nodes
+tagNode = element tagNode
+{
+    nodeNameAttr,
+    (properties? & children & command?)
+}
+
+# Leaf nodes are terminal configuration nodes that can't have children,
+# but can have values.
+
+leafNode = element leafNode
+{
+    nodeNameAttr,
+    command,
+    properties
+}
+
+# Normal and tag nodes may have children
+children = element children
+{
+    (node | tagNode | leafNode)+
+}
+
+# Nodes may have properties
+# For simplicity, any property is allowed in any node,
+# but whether they are used or not is implementation-defined
+
+
+properties = element properties
+{
+    help? &
+    completionHelp*
+}
+
+# All nodes must have "name" attribute
+nodeNameAttr = attribute name
+{
+    text
+}
+
+
+
+
+
+# help tags contains brief description of the purpose of the node
+help = element help
+{
+    text
+}
+
+command = element command
+{
+    text
+}
+
+# completionHelp tags contain information about allowed values of a node that is used for generating
+# tab completion in the CLI frontend and drop-down lists in GUI frontends
+# It is only meaninful for leaf nodes
+# Allowed values can be given as a fixed list of values (e.g. <list>foo bar baz</list>),
+# as a configuration path (e.g. <path>interfaces ethernet</path>),
+# or as a path to a script file that generates the list (e.g. <script>/usr/lib/foo/list-things</script>
+completionHelp = element completionHelp
+{
+    (element list { text })* &
+    (element path { text })* &
+    (element script { text })*
+}
\ No newline at end of file
diff --git a/schema/op-mode-definition.rng b/schema/op-mode-definition.rng
new file mode 100644
index 000000000..98a231e53
--- /dev/null
+++ b/schema/op-mode-definition.rng
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+  <!--
+       interface_definition.rnc: VyConf reference tree XML grammar
+    
+       Copyright (C) 2014. 2017 VyOS maintainers and contributors <maintainers@vyos.net>
+    
+       This library is free software; you can redistribute it and/or
+       modify it under the terms of the GNU Lesser General Public
+       License as published by the Free Software Foundation; either
+       version 2.1 of the License, or (at your option) any later version.
+    
+       This library is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+       Lesser General Public License for more details.
+    
+       You should have received a copy of the GNU Lesser General Public
+       License along with this library; if not, write to the Free Software
+       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+       USA
+  -->
+  <!--
+    The language of this file is compact form RELAX-NG
+    http://relaxng.org/compact-tutorial-20030326.htm
+    (unless converted to XML, then just RELAX-NG :)
+  -->
+  <!-- Interface definition starts with interfaceDefinition tag that may contain node tags -->
+  <start>
+    <element name="interfaceDefinition">
+      <zeroOrMore>
+        <ref name="node"/>
+      </zeroOrMore>
+    </element>
+  </start>
+  <!--
+    node tag may contain node, leafNode, or tagNode tags
+    Those are intermediate configuration nodes that may only contain
+    other nodes and must not have values
+  -->
+  <define name="node">
+    <element name="node">
+      <ref name="nodeNameAttr"/>
+      <interleave>
+        <optional>
+          <ref name="properties"/>
+        </optional>
+        <optional>
+          <ref name="children"/>
+        </optional>
+        <optional>
+          <ref name="command"/>
+        </optional>
+      </interleave>
+    </element>
+  </define>
+  <!--
+    Tag nodes are containers for nodes without predefined names, like network interfaces
+    or user names (e.g. "interfaces ethernet eth0" or "user jrandomhacker")
+    Tag nodes may contain node and leafNode elements, and also nameConstraint tags
+    They must not contain other tag nodes
+  -->
+  <define name="tagNode">
+    <element name="tagNode">
+      <ref name="nodeNameAttr"/>
+      <interleave>
+        <optional>
+          <ref name="properties"/>
+        </optional>
+        <ref name="children"/>
+        <optional>
+          <ref name="command"/>
+        </optional>
+      </interleave>
+    </element>
+  </define>
+  <!--
+    Leaf nodes are terminal configuration nodes that can't have children,
+    but can have values.
+  -->
+  <define name="leafNode">
+    <element name="leafNode">
+      <ref name="nodeNameAttr"/>
+      <ref name="command"/>
+      <ref name="properties"/>
+    </element>
+  </define>
+  <!-- Normal and tag nodes may have children -->
+  <define name="children">
+    <element name="children">
+      <oneOrMore>
+        <choice>
+          <ref name="node"/>
+          <ref name="tagNode"/>
+          <ref name="leafNode"/>
+        </choice>
+      </oneOrMore>
+    </element>
+  </define>
+  <!--
+    Nodes may have properties
+    For simplicity, any property is allowed in any node,
+    but whether they are used or not is implementation-defined
+  -->
+  <define name="properties">
+    <element name="properties">
+      <interleave>
+        <optional>
+          <ref name="help"/>
+        </optional>
+        <zeroOrMore>
+          <ref name="completionHelp"/>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+  <!-- All nodes must have "name" attribute -->
+  <define name="nodeNameAttr">
+    <attribute name="name"/>
+  </define>
+  <!-- help tags contains brief description of the purpose of the node -->
+  <define name="help">
+    <element name="help">
+      <text/>
+    </element>
+  </define>
+  <define name="command">
+    <element name="command">
+      <text/>
+    </element>
+  </define>
+  <!--
+    completionHelp tags contain information about allowed values of a node that is used for generating
+    tab completion in the CLI frontend and drop-down lists in GUI frontends
+    It is only meaninful for leaf nodes
+    Allowed values can be given as a fixed list of values (e.g. <list>foo bar baz</list>),
+    as a configuration path (e.g. <path>interfaces ethernet</path>),
+    or as a path to a script file that generates the list (e.g. <script>/usr/lib/foo/list-things</script>
+  -->
+  <define name="completionHelp">
+    <element name="completionHelp">
+      <interleave>
+        <zeroOrMore>
+          <element name="list">
+            <text/>
+          </element>
+        </zeroOrMore>
+        <zeroOrMore>
+          <element name="path">
+            <text/>
+          </element>
+        </zeroOrMore>
+        <zeroOrMore>
+          <element name="script">
+            <text/>
+          </element>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+</grammar>
diff --git a/scripts/build-command-op-templates b/scripts/build-command-op-templates
new file mode 100755
index 000000000..6b6688fba
--- /dev/null
+++ b/scripts/build-command-op-templates
@@ -0,0 +1,208 @@
+#!/usr/bin/env python3
+#
+#    build-command-template: converts new style command definitions in XML
+#      to the old style (bunch of dirs and node.def's) command templates
+#
+#    Copyright (C) 2017 VyOS maintainers <maintainers@vyos.net>
+#
+#    This library is free software; you can redistribute it and/or
+#    modify it under the terms of the GNU Lesser General Public
+#    License as published by the Free Software Foundation; either
+#    version 2.1 of the License, or (at your option) any later version.
+#
+#    This library is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#    Lesser General Public License for more details.
+#
+#    You should have received a copy of the GNU Lesser General Public
+#    License along with this library; if not, write to the Free Software
+#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+#    USA
+
+import sys
+import os
+import argparse
+import copy
+import functools
+
+from lxml import etree as ET
+
+# Defaults
+
+validator_dir = "/opt/vyatta/libexec/validators"
+default_constraint_err_msg = "Invalid value"
+
+
+## Get arguments
+
+parser = argparse.ArgumentParser(description='Converts new-style XML interface definitions to old-style command templates')
+parser.add_argument('--debug', help='Enable debug information output', action='store_true')
+parser.add_argument('INPUT_FILE', type=str, help="XML interface definition file")
+parser.add_argument('SCHEMA_FILE', type=str, help="RelaxNG schema file")
+parser.add_argument('OUTPUT_DIR', type=str, help="Output directory")
+
+args = parser.parse_args()
+
+input_file = args.INPUT_FILE
+schema_file = args.SCHEMA_FILE
+output_dir = args.OUTPUT_DIR
+debug = args.debug
+
+## Load and validate the inputs
+
+try:
+    xml = ET.parse(input_file)
+except Exception as e:
+    print("Failed to load interface definition file {0}".format(input_file))
+    print(e)
+    sys.exit(1)
+
+try:
+    relaxng_xml = ET.parse(schema_file)
+    validator = ET.RelaxNG(relaxng_xml)
+
+    if not validator.validate(xml):
+        print(validator.error_log)
+        print("Interface definition file {0} does not match the schema!".format(input_file))
+        sys.exit(1)
+except Exception as e:
+    print("Failed to load the XML schema {0}".format(schema_file))
+    print(e)
+    sys.exit(1)
+
+if not os.access(output_dir, os.W_OK):
+    print("The output directory {0} is not writeable".format(output_dir))
+    sys.exit(1)
+
+## If we got this far, everything must be ok and we can convert the file
+
+def make_path(l):
+    path = functools.reduce(os.path.join, l)
+    if debug:
+        print(path)
+    return path
+
+def get_properties(p):
+    props = {}
+
+    if p is None:
+        return props
+
+    # Get the help string
+    try:
+        props["help"] = p.find("help").text
+    except:
+        pass
+
+
+    # Get the completion help strings
+    try:
+        che = p.findall("completionHelp")
+        ch = ""
+        for c in che:
+            scripts = c.findall("script")
+            paths = c.findall("path")
+            lists = c.findall("list")
+
+            # Current backend doesn't support multiple allowed: tags
+            # so we get to emulate it
+            comp_exprs = []
+            for i in lists:
+                comp_exprs.append("echo \"{0}\"".format(i.text))
+            for i in paths:
+                comp_exprs.append("/bin/cli-shell-api listNodes {0}".format(i.text))
+            for i in scripts:
+                comp_exprs.append("sh -c \"{0}\"".format(i.text))
+            comp_help = " && ".join(comp_exprs)
+            props["comp_help"] = comp_help
+    except:
+        props["comp_help"] = []
+
+    return props
+
+
+def make_node_def(props, command):
+    # XXX: replace with a template processor if it grows
+    #      out of control
+
+    node_def = ""
+
+    if "tag" in props:
+        node_def += "tag:\n"
+
+
+    if "type" in props:
+        node_def += "type: {0}\n".format(props["type"])
+
+
+    if "help" in props:
+        node_def += "help: {0}\n".format(props["help"])
+
+
+    if "comp_help" in props:
+        node_def += "allowed: {0}\n".format(props["comp_help"])
+
+
+    if command is not None:
+        node_def += "run: sudo sh -c {0}\n".format(command.text)
+
+
+    if debug:
+        print("The contents of the node.def file:\n", node_def)
+
+    return node_def
+
+def process_node(n, tmpl_dir):
+    # Avoid mangling the path from the outer call
+    my_tmpl_dir = copy.copy(tmpl_dir)
+
+    props_elem = n.find("properties")
+    children = n.find("children")
+    command = n.find("command")
+
+    name = n.get("name")
+
+    node_type = n.tag
+
+    my_tmpl_dir.append(name)
+
+    if debug:
+        print("Name of the node: {};\n Created directory: ".format(name), end="")
+    os.makedirs(make_path(my_tmpl_dir), exist_ok=True)
+
+    props = get_properties(props_elem)
+
+    # Type should not be set for non-tag, non-leaf nodes
+    if node_type != "node":
+        props["type"] = "txt"
+    if node_type == "tagNode":
+        props["tag"] = "True"
+    
+
+    with open(os.path.join(make_path(my_tmpl_dir), "node.def"), "w") as f:
+        f.write(make_node_def(props, command))
+
+
+    if node_type == "node":
+        inner_nodes = children.iterfind("*")
+        for inner_n in inner_nodes:
+            process_node(inner_n, my_tmpl_dir)
+    if node_type == "tagNode":
+        my_tmpl_dir.append("node.tag")
+        if debug:
+            print("Created path for the tagNode:", end="")
+        os.makedirs(make_path(my_tmpl_dir), exist_ok=True)
+        inner_nodes = children.iterfind("*")
+        for inner_n in inner_nodes:
+            process_node(inner_n, my_tmpl_dir)
+    else:
+        # This is a leaf node
+        pass
+
+
+root = xml.getroot()
+
+nodes = root.iterfind("*")
+for n in nodes:
+    process_node(n, [output_dir])