summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--data/templates/frr/rip.frr.tmpl143
-rw-r--r--debian/control2
-rwxr-xr-xdebian/rules4
-rw-r--r--interface-definitions/protocols-rip.xml.in2
-rw-r--r--interface-definitions/service_console-server.xml.in3
-rw-r--r--interface-definitions/ssh.xml.in21
-rw-r--r--python/setup.py10
-rw-r--r--python/vyos/ifconfig/wireguard.py58
-rw-r--r--python/vyos/xml/.gitignore1
-rw-r--r--python/vyos/xml/__init__.py39
-rw-r--r--python/vyos/xml/cache/__init__.py0
-rw-r--r--python/vyos/xml/definition.py301
-rwxr-xr-xpython/vyos/xml/generate.py70
-rw-r--r--python/vyos/xml/kw.py83
-rw-r--r--python/vyos/xml/load.py290
-rw-r--r--python/vyos/xml/test_xml.py279
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py9
-rwxr-xr-xsrc/conf_mode/protocols_rip.py317
-rwxr-xr-xsrc/conf_mode/service_console-server.py9
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py2
-rwxr-xr-xsrc/conf_mode/vpn_l2tp.py2
-rwxr-xr-xsrc/conf_mode/vpn_pptp.py2
-rwxr-xr-xsrc/conf_mode/vpn_sstp.py2
24 files changed, 1594 insertions, 56 deletions
diff --git a/Makefile b/Makefile
index 593a8a5f7..d74df8538 100644
--- a/Makefile
+++ b/Makefile
@@ -73,7 +73,6 @@ interface_definitions: $(BUILD_DIR) $(obj)
rm -f $(TMPL_DIR)/interfaces/wirelessmodem/node.tag/ipv6/node.def
rm -f $(TMPL_DIR)/protocols/node.def
rm -rf $(TMPL_DIR)/protocols/nbgp
- rm -rf $(TMPL_DIR)/protocols/nrip
rm -rf $(TMPL_DIR)/protocols/isis
rm -f $(TMPL_DIR)/protocols/static/node.def
rm -f $(TMPL_DIR)/system/node.def
diff --git a/data/templates/frr/rip.frr.tmpl b/data/templates/frr/rip.frr.tmpl
new file mode 100644
index 000000000..60bc686bd
--- /dev/null
+++ b/data/templates/frr/rip.frr.tmpl
@@ -0,0 +1,143 @@
+!
+{% if rip_conf -%}
+router rip
+{% if old_default_distance -%}
+no distance {{old_default_distance}}
+{% endif -%}
+{% if default_distance -%}
+distance {{default_distance}}
+{% endif -%}
+{% if old_default_originate -%}
+no default-information originate
+{% endif -%}
+{% if default_originate -%}
+default-information originate
+{% endif -%}
+{% if old_rip.default_metric -%}
+no default-metric {{old_rip.default_metric}}
+{% endif -%}
+{% if rip.default_metric -%}
+default-metric {{rip.default_metric}}
+{% endif -%}
+{% for protocol in old_rip.redist -%}
+{% if old_rip.redist[protocol]['metric'] and old_rip.redist[protocol]['route_map'] -%}
+no redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}} route-map {{rip.redist[protocol]['route_map']}}
+{% elif old_rip.redist[protocol]['metric'] -%}
+no redistribute {{protocol}} metric {{old_rip.redist[protocol]['metric']}}
+{% elif old_rip.redist[protocol]['route_map'] -%}
+no redistribute {{protocol}} route-map {{old_rip.redist[protocol]['route_map']}}
+{% else -%}
+no redistribute {{protocol}}
+{% endif -%}
+{% endfor -%}
+{% for protocol in rip.redist -%}
+{% if rip.redist[protocol]['metric'] and rip.redist[protocol]['route_map'] -%}
+redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}} route-map {{rip.redist[protocol]['route_map']}}
+{% elif rip.redist[protocol]['metric'] -%}
+redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}}
+{% elif rip.redist[protocol]['route_map'] -%}
+redistribute {{protocol}} route-map {{rip.redist[protocol]['route_map']}}
+{% else -%}
+redistribute {{protocol}}
+{% endif -%}
+{% endfor -%}
+{% for iface in old_rip.distribute -%}
+{% if old_rip.distribute[iface].iface_access_list_in -%}
+no distribute-list {{old_rip.distribute[iface].iface_access_list_in}} in {{iface}}
+{% endif -%}
+{% if old_rip.distribute[iface].iface_access_list_out -%}
+no distribute-list {{old_rip.distribute[iface].iface_access_list_out}} out {{iface}}
+{% endif -%}
+{% if old_rip.distribute[iface].iface_prefix_list_in -%}
+no distribute-list prefix {{old_rip.distribute[iface].iface_prefix_list_in}} in {{iface}}
+{% endif -%}
+{% if old_rip.distribute[iface].iface_prefix_list_out -%}
+no distribute-list prefix {{old_rip.distribute[iface].iface_prefix_list_out}} out {{iface}}
+{% endif -%}
+{% endfor -%}
+{% for iface in rip.distribute -%}
+{% if rip.distribute[iface].iface_access_list_in -%}
+distribute-list {{rip.distribute[iface].iface_access_list_in}} in {{iface}}
+{% endif -%}
+{% if rip.distribute[iface].iface_access_list_out -%}
+distribute-list {{rip.distribute[iface].iface_access_list_out}} out {{iface}}
+{% endif -%}
+{% if rip.distribute[iface].iface_prefix_list_in -%}
+distribute-list prefix {{rip.distribute[iface].iface_prefix_list_in}} in {{iface}}
+{% endif -%}
+{% if rip.distribute[iface].iface_prefix_list_out -%}
+distribute-list prefix {{rip.distribute[iface].iface_prefix_list_out}} out {{iface}}
+{% endif -%}
+{% endfor -%}
+{% if old_rip.dist_acl_in -%}
+no distribute-list {{old_rip.dist_acl_in}} in
+{% endif -%}
+{% if rip.dist_acl_in -%}
+distribute-list {{rip.dist_acl_in}} in
+{% endif -%}
+{% if old_rip.dist_acl_out -%}
+no distribute-list {{old_rip.dist_acl_out}} out
+{% endif -%}
+{% if rip.dist_acl_out -%}
+distribute-list {{rip.dist_acl_out}} out
+{% endif -%}
+{% if old_rip.dist_prfx_in -%}
+no distribute-list prefix {{old_rip.dist_prfx_in}} in
+{% endif -%}
+{% if rip.dist_prfx_in -%}
+distribute-list prefix {{rip.dist_prfx_in}} in
+{% endif -%}
+{% if old_rip.dist_prfx_out -%}
+no distribute-list prefix {{old_rip.dist_prfx_out}} out
+{% endif -%}
+{% if rip.dist_prfx_out -%}
+distribute-list prefix {{rip.dist_prfx_out}} out
+{% endif -%}
+{% for network in old_rip.networks -%}
+no network {{network}}
+{% endfor -%}
+{% for network in rip.networks -%}
+network {{network}}
+{% endfor -%}
+{% for iface in old_rip.ifaces -%}
+no network {{iface}}
+{% endfor -%}
+{% for iface in rip.ifaces -%}
+network {{iface}}
+{% endfor -%}
+{% for neighbor in old_rip.neighbors -%}
+no neighbor {{neighbor}}
+{% endfor -%}
+{% for neighbor in rip.neighbors -%}
+neighbor {{neighbor}}
+{% endfor -%}
+{% for net in rip.net_distance -%}
+{% if rip.net_distance[net].access_list and rip.net_distance[net].distance -%}
+distance {{rip.net_distance[net].distance}} {{net}} {{rip.net_distance[net].access_list}}
+{% else -%}
+distance {{rip.net_distance[net].distance}} {{net}}
+{% endif -%}
+{% endfor -%}
+{% for passive_iface in old_rip.passive_iface -%}
+no passive-interface {{passive_iface}}
+{% endfor -%}
+{% for passive_iface in rip.passive_iface -%}
+passive-interface {{passive_iface}}
+{% endfor -%}
+{% for route in old_rip.route -%}
+no route {{route}}
+{% endfor -%}
+{% for route in rip.route -%}
+route {{route}}
+{% endfor -%}
+{% if old_rip.timer_update or old_rip.timer_timeout or old_rip.timer_garbage -%}
+no timers basic
+{% endif -%}
+{% if rip.timer_update or rip.timer_timeout or rip.timer_garbage -%}
+timers basic {{rip.timer_update}} {{rip.timer_timeout}} {{rip.timer_garbage}}
+{% endif -%}
+!
+{% else -%}
+no router rip
+!
+{% endif -%}
diff --git a/debian/control b/debian/control
index aaaf33e2a..5e14340a8 100644
--- a/debian/control
+++ b/debian/control
@@ -6,7 +6,7 @@ Build-Depends: debhelper (>= 9),
quilt,
python3,
python3-setuptools,
- quilt,
+ python3-xmltodict,
python3-lxml,
python3-nose,
python3-coverage,
diff --git a/debian/rules b/debian/rules
index 3e408b538..c080b8633 100755
--- a/debian/rules
+++ b/debian/rules
@@ -23,6 +23,10 @@ override_dh_auto_build:
override_dh_auto_install:
dh_auto_install
+
+ # convert the XML to dictionaries
+ env PYTHONPATH=python python3 python/vyos/xml/generate.py
+
cd python; python3 setup.py install --install-layout=deb --root ../$(DIR); cd ..
# Install scripts
diff --git a/interface-definitions/protocols-rip.xml.in b/interface-definitions/protocols-rip.xml.in
index a9c295f4c..107f0e0d5 100644
--- a/interface-definitions/protocols-rip.xml.in
+++ b/interface-definitions/protocols-rip.xml.in
@@ -2,7 +2,7 @@
<interfaceDefinition>
<node name="protocols">
<children>
- <node name="nrip" owner="${vyos_conf_scripts_dir}/protocols_rip.py">
+ <node name="rip" owner="${vyos_conf_scripts_dir}/protocols_rip.py">
<properties>
<help>Routing Information Protocol (RIP) parameters</help>
</properties>
diff --git a/interface-definitions/service_console-server.xml.in b/interface-definitions/service_console-server.xml.in
index 348d591dd..59a9fe237 100644
--- a/interface-definitions/service_console-server.xml.in
+++ b/interface-definitions/service_console-server.xml.in
@@ -50,6 +50,7 @@
<regex>(7|8)</regex>
</constraint>
</properties>
+ <defaultValue>8</defaultValue>
</leafNode>
<leafNode name="stop-bits">
<properties>
@@ -61,6 +62,7 @@
<regex>(1|2)</regex>
</constraint>
</properties>
+ <defaultValue>1</defaultValue>
</leafNode>
<leafNode name="parity">
<properties>
@@ -72,6 +74,7 @@
<regex>(even|odd|none)</regex>
</constraint>
</properties>
+ <defaultValue>none</defaultValue>
</leafNode>
<node name="ssh">
<properties>
diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in
index de926a897..4adfaecfb 100644
--- a/interface-definitions/ssh.xml.in
+++ b/interface-definitions/ssh.xml.in
@@ -5,7 +5,7 @@
<children>
<node name="ssh" owner="${vyos_conf_scripts_dir}/ssh.py">
<properties>
- <help>Secure SHell (SSH) protocol</help>
+ <help>Secure Shell (SSH)</help>
<priority>500</priority>
</properties>
<children>
@@ -76,8 +76,12 @@
<properties>
<help>Allowed ciphers</help>
<completionHelp>
- <script>ssh -Q cipher | tr '\n' ' '</script>
+ <!-- generated by ssh -Q cipher | tr '\n' ' ' as this will not change dynamically -->
+ <list>3des-cbc aes128-cbc aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se aes128-ctr aes192-ctr aes256-ctr aes128-gcm@openssh.com aes256-gcm@openssh.com chacha20-poly1305@openssh.com</list>
</completionHelp>
+ <constraint>
+ <regex>^(3des-cbc|aes128-cbc|aes192-cbc|aes256-cbc|rijndael-cbc@lysator.liu.se|aes128-ctr|aes192-ctr|aes256-ctr|aes128-gcm@openssh.com|aes256-gcm@openssh.com|chacha20-poly1305@openssh.com)$</regex>
+ </constraint>
<multi/>
</properties>
</leafNode>
@@ -97,9 +101,13 @@
<properties>
<help>Allowed key exchange (KEX) algorithms</help>
<completionHelp>
- <script>ssh -Q kex | tr '\n' ' '</script>
+ <!-- generated by ssh -Q kex | tr '\n' ' ' as this will not change dynamically -->
+ <list>diffie-hellman-group1-sha1 diffie-hellman-group14-sha1 diffie-hellman-group14-sha256 diffie-hellman-group16-sha512 diffie-hellman-group18-sha512 diffie-hellman-group-exchange-sha1 diffie-hellman-group-exchange-sha256 ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 curve25519-sha256 curve25519-sha256@libssh.org</list>
</completionHelp>
<multi/>
+ <constraint>
+ <regex>^(diffie-hellman-group1-sha1|diffie-hellman-group14-sha1|diffie-hellman-group14-sha256|diffie-hellman-group16-sha512|diffie-hellman-group18-sha512|diffie-hellman-group-exchange-sha1|diffie-hellman-group-exchange-sha256|ecdh-sha2-nistp256|ecdh-sha2-nistp384|ecdh-sha2-nistp521|curve25519-sha256|curve25519-sha256@libssh.org)$</regex>
+ </constraint>
</properties>
</leafNode>
<leafNode name="listen-address">
@@ -144,13 +152,18 @@
<description>enable logging of failed login attempts</description>
</valueHelp>
</properties>
+ <defaultValue>INFO</defaultValue>
</leafNode>
<leafNode name="mac">
<properties>
<help>Allowed message authentication code (MAC) algorithms</help>
<completionHelp>
- <script>ssh -Q mac | tr '\n' ' '</script>
+ <!-- generated by ssh -Q mac | tr '\n' ' ' as this will not change dynamically -->
+ <list>hmac-sha1 hmac-sha1-96 hmac-sha2-256 hmac-sha2-512 hmac-md5 hmac-md5-96 umac-64@openssh.com umac-128@openssh.com hmac-sha1-etm@openssh.com hmac-sha1-96-etm@openssh.com hmac-sha2-256-etm@openssh.com hmac-sha2-512-etm@openssh.com hmac-md5-etm@openssh.com hmac-md5-96-etm@openssh.com umac-64-etm@openssh.com umac-128-etm@openssh.com</list>
</completionHelp>
+ <constraint>
+ <regex>^(hmac-sha1|hmac-sha1-96|hmac-sha2-256|hmac-sha2-512|hmac-md5|hmac-md5-96|umac-64@openssh.com|umac-128@openssh.com|hmac-sha1-etm@openssh.com|hmac-sha1-96-etm@openssh.com|hmac-sha2-256-etm@openssh.com|hmac-sha2-512-etm@openssh.com|hmac-md5-etm@openssh.com|hmac-md5-96-etm@openssh.com|umac-64-etm@openssh.com|umac-128-etm@openssh.com)$</regex>
+ </constraint>
<multi/>
</properties>
</leafNode>
diff --git a/python/setup.py b/python/setup.py
index 9440e7fe7..e2d28bd6b 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -1,6 +1,13 @@
import os
from setuptools import setup
+def packages(directory):
+ return [
+ _[0].replace('/','.')
+ for _ in os.walk(directory)
+ if os.path.isfile(os.path.join(_[0], '__init__.py'))
+ ]
+
setup(
name = "vyos",
version = "1.3.0",
@@ -10,7 +17,7 @@ setup(
license = "LGPLv2+",
keywords = "vyos",
url = "http://www.vyos.io",
- packages=["vyos","vyos.ifconfig"],
+ packages = packages('vyos'),
long_description="VyOS configuration libraries",
classifiers=[
"Development Status :: 4 - Beta",
@@ -18,4 +25,3 @@ setup(
"License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)",
],
)
-
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index 027b5ea8c..a90a66ac3 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -149,10 +149,10 @@ class WireGuardIf(Interface):
default = {
'type': 'wireguard',
'port': 0,
- 'private-key': None,
+ 'private_key': None,
'pubkey': None,
- 'psk': '/dev/null',
- 'allowed-ips': [],
+ 'psk': '',
+ 'allowed_ips': [],
'fwmark': 0x00,
'endpoint': None,
'keepalive': 0
@@ -166,8 +166,8 @@ class WireGuardIf(Interface):
}
}
options = Interface.options + \
- ['port', 'private-key', 'pubkey', 'psk',
- 'allowed-ips', 'fwmark', 'endpoint', 'keepalive']
+ ['port', 'private_key', 'pubkey', 'psk',
+ 'allowed_ips', 'fwmark', 'endpoint', 'keepalive']
"""
Wireguard interface class, contains a comnfig dictionary since
@@ -180,44 +180,44 @@ class WireGuardIf(Interface):
>>> from vyos.ifconfig import WireGuardIf as wg_if
>>> wg_intfc = wg_if("wg01")
>>> print (wg_intfc.wg_config)
- {'private-key': None, 'keepalive': 0, 'endpoint': None, 'port': 0,
- 'allowed-ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'}
+ {'private_key': None, 'keepalive': 0, 'endpoint': None, 'port': 0,
+ 'allowed_ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'}
>>> wg_intfc.wg_config['keepalive'] = 100
>>> print (wg_intfc.wg_config)
- {'private-key': None, 'keepalive': 100, 'endpoint': None, 'port': 0,
- 'allowed-ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'}
+ {'private_key': None, 'keepalive': 100, 'endpoint': None, 'port': 0,
+ 'allowed_ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'}
"""
def update(self):
- if not self.config['private-key']:
+ if not self.config['private_key']:
raise ValueError("private key required")
else:
# fmask permission check?
pass
- cmd = "wg set {} ".format(self.config['ifname'])
- cmd += "listen-port {} ".format(self.config['port'])
- cmd += "fwmark {} ".format(str(self.config['fwmark']))
- cmd += "private-key {} ".format(self.config['private-key'])
- cmd += "peer {} ".format(self.config['pubkey'])
- cmd += " preshared-key {} ".format(self.config['psk'])
- cmd += " allowed-ips "
- for aip in self.config['allowed-ips']:
- if aip != self.config['allowed-ips'][-1]:
- cmd += aip + ","
- else:
- cmd += aip
+ cmd = 'wg set {ifname}'.format(**self.config)
+ cmd += ' listen-port {port}'.format(**self.config)
+ cmd += ' fwmark "{fwmark}" '.format(**self.config)
+ cmd += ' private-key {private_key}'.format(**self.config)
+ cmd += ' peer {pubkey}'.format(**self.config)
+ cmd += ' persistent-keepalive {keepalive}'.format(**self.config)
+ cmd += ' allowed-ips {}'.format(', '.join(self.config['allowed-ips']))
+
if self.config['endpoint']:
- cmd += " endpoint '{}'".format(self.config['endpoint'])
- cmd += " persistent-keepalive {}".format(self.config['keepalive'])
+ cmd += ' endpoint "{endpoint}"'.format(**self.config)
+
+ psk_file = ''
+ if self.config['psk']:
+ psk_file = '/tmp/{ifname}.psk'.format(**self.config)
+ with open(psk_file, 'w') as f:
+ f.write(self.config['psk'])
+ cmd += f' preshared-key {psk_file}'
self._cmd(cmd)
- # remove psk since it isn't required anymore and is saved in the cli
- # config only !!
- if self.config['psk'] != '/dev/null':
- if os.path.exists(self.config['psk']):
- os.remove(self.config['psk'])
+ # PSK key file is not required to be stored persistently as its backed by CLI
+ if os.path.exists(psk_file):
+ os.remove(psk_file)
def remove_peer(self, peerkey):
"""
diff --git a/python/vyos/xml/.gitignore b/python/vyos/xml/.gitignore
new file mode 100644
index 000000000..e934adfd1
--- /dev/null
+++ b/python/vyos/xml/.gitignore
@@ -0,0 +1 @@
+cache/
diff --git a/python/vyos/xml/__init__.py b/python/vyos/xml/__init__.py
new file mode 100644
index 000000000..52f5bfb38
--- /dev/null
+++ b/python/vyos/xml/__init__.py
@@ -0,0 +1,39 @@
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+from vyos.xml import definition
+from vyos.xml import load
+from vyos.xml import kw
+
+
+def load_configuration(cache=[]):
+ if cache:
+ return cache[0]
+
+ xml = definition.XML()
+
+ try:
+ from vyos.xml.cache import configuration
+ xml.update(configuration.definition)
+ cache.append(xml)
+ except Exception:
+ xml = definition.XML()
+ print('no xml configuration cache')
+ xml.update(load.xml(load.configuration_definition))
+
+ return xml
+
+
+def defaults(lpath):
+ return load_configuration().defaults(lpath)
diff --git a/python/vyos/xml/cache/__init__.py b/python/vyos/xml/cache/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/python/vyos/xml/cache/__init__.py
diff --git a/python/vyos/xml/definition.py b/python/vyos/xml/definition.py
new file mode 100644
index 000000000..c5f6b0fc7
--- /dev/null
+++ b/python/vyos/xml/definition.py
@@ -0,0 +1,301 @@
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+from vyos.xml import kw
+
+# As we index by key, the name is first and then the data:
+# {'dummy': {
+# '[node]': '[tagNode]',
+# 'address': { ... }
+# } }
+
+# so when we encounter a tagNode, we are really encountering
+# the tagNode data.
+
+
+class XML(dict):
+ def __init__(self):
+ self[kw.tree] = {}
+ self[kw.priorities] = {}
+ self[kw.owners] = {}
+ self[kw.default] = {}
+ self[kw.tags] = []
+
+ dict.__init__(self)
+
+ self.tree = self[kw.tree]
+ # the options which matched the last incomplete world we had
+ # or the last word in a list
+ self.options = []
+ # store all the part of the command we processed
+ self.inside = []
+ # should we check the data pass with the constraints
+ self.check = False
+ # are we still typing a word
+ self.filling = False
+ # do what have the tagNode value ?
+ self.filled = False
+ # last word seen
+ self.word = ''
+ # do we have all the data we want ?
+ self.final = False
+ # do we have too much data ?
+ self.extra = False
+ # what kind of node are we in plain vs data not
+ self.plain = True
+
+ def reset(self):
+ self.tree = self[kw.tree]
+ self.options = []
+ self.inside = []
+ self.check = False
+ self.filling = False
+ self.filled = False
+ self.word = ''
+ self.final = False
+ self.extra = False
+ self.plain = True
+
+ # from functools import lru_cache
+ # @lru_cache(maxsize=100)
+ # XXX: need to use cachetool instead - for later
+
+ def traverse(self, cmd):
+ self.reset()
+
+ # using split() intead of split(' ') eats the final ' '
+ words = cmd.split(' ')
+ passed = []
+ word = ''
+ data_node = False
+ space = False
+
+ while words:
+ word = words.pop(0)
+ space = word == ''
+ perfect = False
+ if word in self.tree:
+ passed = []
+ perfect = True
+ self.tree = self.tree[word]
+ data_node = self.tree[kw.node]
+ self.inside.append(word)
+ word = ''
+ continue
+ if word and data_node:
+ passed.append(word)
+
+ is_valueless = self.tree.get(kw.valueless, False)
+ is_leafNode = data_node == kw.leafNode
+ is_dataNode = data_node in (kw.leafNode, kw.tagNode)
+ named_options = [_ for _ in self.tree if not kw.found(_)]
+
+ if is_leafNode:
+ self.final = is_valueless or len(passed) > 0
+ self.extra = is_valueless and len(passed) > 0
+ self.check = len(passed) >= 1
+ else:
+ self.final = False
+ self.extra = False
+ self.check = len(passed) == 1 and not space
+
+ if self.final:
+ self.word = ' '.join(passed)
+ else:
+ self.word = word
+
+ if self.final:
+ self.filling = True
+ else:
+ self.filling = not perfect and bool(cmd and word != '')
+
+ self.filled = self.final or (is_dataNode and len(passed) > 0 and word == '')
+
+ if is_dataNode and len(passed) == 0:
+ self.options = []
+ elif word:
+ if data_node != kw.plainNode or len(passed) == 1:
+ self.options = [_ for _ in self.tree if _.startswith(word)]
+ else:
+ self.options = []
+ else:
+ self.options = named_options
+
+ self.plain = not is_dataNode
+
+ # self.debug()
+
+ return self.word
+
+ def speculate(self):
+ if len(self.options) == 1:
+ self.tree = self.tree[self.options[0]]
+ self.word = ''
+ if self.tree.get(kw.node,'') not in (kw.tagNode, kw.leafNode):
+ self.options = [_ for _ in self.tree if not kw.found(_)]
+
+ def checks(self, cmd):
+ # as we move thought the named node twice
+ # the first time we get the data with the node
+ # and the second with the pass parameters
+ xml = self[kw.tree]
+
+ words = cmd.split(' ')
+ send = True
+ last = []
+ while words:
+ word = words.pop(0)
+ if word in xml:
+ xml = xml[word]
+ send = True
+ last = []
+ continue
+ if xml[kw.node] in (kw.tagNode, kw.leafNode):
+ if kw.constraint in xml:
+ if send:
+ yield (word, xml[kw.constraint])
+ send = False
+ else:
+ last.append((word, None))
+ if len(last) >= 2:
+ yield last[0]
+
+ def summary(self):
+ yield ('enter', '[ summary ]', str(self.inside))
+
+ if kw.help not in self.tree:
+ yield ('skip', '[ summary ]', str(self.inside))
+ return
+
+ if self.filled:
+ return
+
+ yield('', '', '\nHelp:')
+
+ if kw.help in self.tree:
+ summary = self.tree[kw.help].get(kw.summary)
+ values = self.tree[kw.help].get(kw.valuehelp, [])
+ if summary:
+ yield(summary, '', '')
+ for value in values:
+ yield(value[kw.format], value[kw.description], '')
+
+ def constraint(self):
+ yield ('enter', '[ constraint ]', str(self.inside))
+
+ if kw.help in self.tree:
+ yield ('skip', '[ constraint ]', str(self.inside))
+ return
+ if kw.error not in self.tree:
+ yield ('skip', '[ constraint ]', str(self.inside))
+ return
+ if not self.word or self.filling:
+ yield ('skip', '[ constraint ]', str(self.inside))
+ return
+
+ yield('', '', '\nData Constraint:')
+
+ yield('', 'constraint', str(self.tree[kw.error]))
+
+ def listing(self):
+ yield ('enter', '[ listing ]', str(self.inside))
+
+ # only show the details when we passed the tagNode data
+ if not self.plain and not self.filled:
+ yield ('skip', '[ listing ]', str(self.inside))
+ return
+
+ yield('', '', '\nPossible completions:')
+
+ options = list(self.tree.keys())
+ options.sort()
+ for option in options:
+ if kw.found(option):
+ continue
+ if not option.startswith(self.word):
+ continue
+ inner = self.tree[option]
+ prefix = '+> ' if inner.get(kw.node, '') != kw.leafNode else ' '
+ if kw.help in inner:
+ h = inner[kw.help]
+ yield (prefix + option, h.get(kw.summary), '')
+
+ def debug(self):
+ print('------')
+ print("word '%s'" % self.word)
+ print("filling " + str(self.filling))
+ print("filled " + str(self.filled))
+ print("final " + str(self.final))
+ print("extra " + str(self.extra))
+ print("plain " + str(self.plain))
+ print("options " + str(self.options))
+
+ # from functools import lru_cache
+ # @lru_cache(maxsize=100)
+ # XXX: need to use cachetool instead - for later
+
+ def defaults(self, lpath):
+ d = self[kw.default]
+ for k in lpath:
+ d = d[k]
+ r = {}
+
+ def _flatten(inside, index, d, r):
+ local = inside[index:]
+ prefix = '_'.join(_.replace('-','_') for _ in local) + '_' if local else ''
+ for k in d:
+ under = prefix + k.replace('-','_')
+ level = inside + [k]
+ if isinstance(d[k],dict):
+ _flatten(level, index, d[k], r)
+ continue
+ if self.is_multi(level):
+ r[under] = [_.strip() for _ in d[k].split(',')]
+ continue
+ r[under] = d[k]
+
+ _flatten(lpath, len(lpath), d, r)
+ return r
+
+ # from functools import lru_cache
+ # @lru_cache(maxsize=100)
+ # XXX: need to use cachetool instead - for later
+
+ def _tree(self, lpath):
+ """
+ returns the part of the tree searched or None if it does not exists
+ """
+ tree = self[kw.tree]
+ spath = lpath.copy()
+ while spath:
+ p = spath.pop(0)
+ if p not in tree:
+ return None
+ tree = tree[p]
+ return tree
+
+ def _get(self, lpath, tag):
+ return self._tree(lpath + [tag])
+
+ def is_multi(self, lpath):
+ return self._get(lpath, kw.multi) is True
+
+ def is_tag(self, lpath):
+ return self._get(lpath, kw.node) == kw.tagNode
+
+ def is_leaf(self, lpath):
+ return self._get(lpath, kw.node) == kw.leafNode
+
+ def exists(self, lpath):
+ return self._get(lpath, kw.node) is not None
diff --git a/python/vyos/xml/generate.py b/python/vyos/xml/generate.py
new file mode 100755
index 000000000..dfbbadd74
--- /dev/null
+++ b/python/vyos/xml/generate.py
@@ -0,0 +1,70 @@
+
+#!/usr/bin/env python3
+
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import os
+import sys
+import pprint
+import argparse
+
+from vyos.xml import kw
+from vyos.xml import load
+
+
+# import json
+# def save_json(fname, loaded):
+# with open(fname, 'w') as w:
+# print(f'saving {fname}')
+# w.write(json.dumps(loaded))
+
+
+def save_dict(fname, loaded):
+ with open(fname, 'w') as w:
+ print(f'saving {fname}')
+ w.write(f'# generated by {__file__}\n\n')
+ w.write('definition = ')
+ w.write(str(loaded))
+
+
+def main():
+ parser = argparse.ArgumentParser(description='generate python file from xml defintions')
+ parser.add_argument('--conf-folder', type=str, default=load.configuration_definition, help='XML interface definition folder')
+ parser.add_argument('--conf-cache', type=str, default=load.configuration_cache, help='python file with the conf mode dict')
+
+ # parser.add_argument('--op-folder', type=str, default=load.operational_definition, help='XML interface definition folder')
+ # parser.add_argument('--op-cache', type=str, default=load.operational_cache, help='python file with the conf mode dict')
+
+ parser.add_argument('--dry', action='store_true', help='dry run, print to screen')
+
+ args = parser.parse_args()
+
+ if os.path.exists(load.configuration_cache):
+ os.remove(load.configuration_cache)
+ # if os.path.exists(load.operational_cache):
+ # os.remove(load.operational_cache)
+
+ conf = load.xml(args.conf_folder)
+ # op = load.xml(args.op_folder)
+
+ if args.dry:
+ pprint.pprint(conf)
+ return
+
+ save_dict(args.conf_cache, conf)
+ # save_dict(args.op_cache, op)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/python/vyos/xml/kw.py b/python/vyos/xml/kw.py
new file mode 100644
index 000000000..c85d9e0fd
--- /dev/null
+++ b/python/vyos/xml/kw.py
@@ -0,0 +1,83 @@
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# all named used as key (keywords) in this module are defined here.
+# using variable name will allow the linter to warn on typos
+# it separates our dict syntax from the xmldict one, making it easy to change
+
+# we are redefining a python keyword "list" for ease
+
+
+def found(word):
+ """
+ is the word following the format for a keyword
+ """
+ return word and word[0] == '[' and word[-1] == ']'
+
+
+# root
+
+version = '(version)'
+tree = '(tree)'
+priorities = '(priorities)'
+owners = '(owners)'
+tags = '(tags)'
+default = '(default)'
+
+# nodes
+
+node = '[node]'
+
+plainNode = '[plainNode]'
+leafNode = '[leafNode]'
+tagNode = '[tagNode]'
+
+owner = '[owner]'
+
+valueless = '[valueless]'
+multi = '[multi]'
+hidden = '[hidden]'
+
+# properties
+
+priority = '[priority]'
+
+completion = '[completion]'
+list = '[list]'
+script = '[script]'
+path = '[path]'
+
+# help
+
+help = '[help]'
+
+summary = '[summary]'
+
+valuehelp = '[valuehelp]'
+format = 'format'
+description = 'description'
+
+# constraint
+
+constraint = '[constraint]'
+name = '[name]'
+
+regex = '[regex]'
+validator = '[validator]'
+argument = '[argument]'
+
+error = '[error]'
+
+# created
+
+node = '[node]'
diff --git a/python/vyos/xml/load.py b/python/vyos/xml/load.py
new file mode 100644
index 000000000..1f463a5b7
--- /dev/null
+++ b/python/vyos/xml/load.py
@@ -0,0 +1,290 @@
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import glob
+
+from os.path import join
+from os.path import abspath
+from os.path import dirname
+
+import xmltodict
+
+from vyos import debug
+from vyos.xml import kw
+from vyos.xml import definition
+
+
+# where the files are located
+
+_here = dirname(__file__)
+
+configuration_definition = abspath(join(_here, '..', '..' ,'..', 'interface-definitions'))
+configuration_cache = abspath(join(_here, 'cache', 'configuration.py'))
+
+operational_definition = abspath(join(_here, '..', '..' ,'..', 'op-mode-definitions'))
+operational_cache = abspath(join(_here, 'cache', 'operational.py'))
+
+
+# This code is only ran during the creation of the debian package
+# therefore we accept that failure can be fatal and not handled
+# gracefully.
+
+
+def _fatal(debug_info=''):
+ """
+ raise a RuntimeError or if in developer mode stop the code
+ """
+ if not debug.enabled('developer'):
+ raise RuntimeError(str(debug_info))
+
+ if debug_info:
+ print(debug_info)
+ breakpoint()
+
+
+def _safe_update(dict1, dict2):
+ """
+ return a dict made of two, raise if any root key would be overwritten
+ """
+ if set(dict1).intersection(dict2):
+ raise RuntimeError('overlapping configuration')
+ return {**dict1, **dict2}
+
+
+def _merge(dict1, dict2):
+ """
+ merge dict2 in to dict1 and return it
+ """
+ for k in list(dict2):
+ if k not in dict1:
+ dict1[k] = dict2[k]
+ continue
+ if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
+ dict1[k] = _merge(dict1[k], dict2[k])
+ elif isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
+ dict1[k].extend(dict2[k])
+ elif dict1[k] == dict2[k]:
+ # A definition shared between multiple files
+ if k in (kw.valueless, kw.multi, kw.hidden, kw.node, kw.summary, kw.owner, kw.priority):
+ continue
+ _fatal()
+ raise RuntimeError('parsing issue - undefined leaf?')
+ else:
+ raise RuntimeError('parsing issue - we messed up?')
+ return dict1
+
+
+def _include(fname, folder=''):
+ """
+ return the content of a file, including any file referenced with a #include
+ """
+ if not folder:
+ folder = dirname(fname)
+ content = ''
+ with open(fname, 'r') as r:
+ for line in r.readlines():
+ if '#include' in line:
+ content += _include(join(folder,line.strip()[10:-1]), folder)
+ continue
+ content += line
+ return content
+
+
+def _format_nodes(inside, conf, xml):
+ r = {}
+ while conf:
+ nodetype = ''
+ nodename = ''
+ if 'node' in conf.keys():
+ nodetype = 'node'
+ nodename = kw.plainNode
+ elif 'leafNode' in conf.keys():
+ nodetype = 'leafNode'
+ nodename = kw.leafNode
+ elif 'tagNode' in conf.keys():
+ nodetype = 'tagNode'
+ nodename = kw.tagNode
+ elif 'syntaxVersion' in conf.keys():
+ r[kw.version] = conf.pop('syntaxVersion')['@version']
+ continue
+ else:
+ _fatal(conf.keys())
+
+ nodes = conf.pop(nodetype)
+ if isinstance(nodes, list):
+ for node in nodes:
+ name = node.pop('@name')
+ into = inside + [name]
+ r[name] = _format_node(into, node, xml)
+ r[name][kw.node] = nodename
+ xml[kw.tags].append(' '.join(into))
+ else:
+ node = nodes
+ name = node.pop('@name')
+ into = inside + [name]
+ r[name] = _format_node(inside + [name], node, xml)
+ r[name][kw.node] = nodename
+ xml[kw.tags].append(' '.join(into))
+ return r
+
+
+def _set_validator(r, validator):
+ v = {}
+ while validator:
+ if '@name' in validator:
+ v[kw.name] = validator.pop('@name')
+ elif '@argument' in validator:
+ v[kw.argument] = validator.pop('@argument')
+ else:
+ _fatal(validator)
+ r[kw.constraint][kw.validator].append(v)
+
+
+def _format_node(inside, conf, xml):
+ r = {
+ kw.valueless: False,
+ kw.multi: False,
+ kw.hidden: False,
+ }
+
+ if '@owner' in conf:
+ owner = conf.pop('@owner', '')
+ r[kw.owner] = owner
+ xml[kw.owners][' '.join(inside)] = owner
+
+ while conf:
+ keys = conf.keys()
+ if 'children' in keys:
+ children = conf.pop('children')
+
+ if isinstance(conf, list):
+ for child in children:
+ r = _safe_update(r, _format_nodes(inside, child, xml))
+ else:
+ child = children
+ r = _safe_update(r, _format_nodes(inside, child, xml))
+
+ elif 'properties' in keys:
+ properties = conf.pop('properties')
+
+ while properties:
+ if 'help' in properties:
+ helpname = properties.pop('help')
+ r[kw.help] = {}
+ r[kw.help][kw.summary] = helpname
+
+ elif 'valueHelp' in properties:
+ valuehelps = properties.pop('valueHelp')
+ if kw.valuehelp in r[kw.help]:
+ _fatal(valuehelps)
+ r[kw.help][kw.valuehelp] = []
+ if isinstance(valuehelps, list):
+ for valuehelp in valuehelps:
+ r[kw.help][kw.valuehelp].append(dict(valuehelp))
+ else:
+ valuehelp = valuehelps
+ r[kw.help][kw.valuehelp].append(dict(valuehelp))
+
+ elif 'constraint' in properties:
+ constraint = properties.pop('constraint')
+ r[kw.constraint] = {}
+ while constraint:
+ if 'regex' in constraint:
+ regexes = constraint.pop('regex')
+ if kw.regex in kw.constraint:
+ _fatal(regexes)
+ r[kw.constraint][kw.regex] = []
+ if isinstance(regexes, list):
+ r[kw.constraint][kw.regex] = []
+ for regex in regexes:
+ r[kw.constraint][kw.regex].append(regex)
+ else:
+ regex = regexes
+ r[kw.constraint][kw.regex].append(regex)
+ elif 'validator' in constraint:
+ validators = constraint.pop('validator')
+ if kw.validator in r[kw.constraint]:
+ _fatal(validators)
+ r[kw.constraint][kw.validator] = []
+ if isinstance(validators, list):
+ for validator in validators:
+ _set_validator(r, validator)
+ else:
+ validator = validators
+ _set_validator(r, validator)
+ else:
+ _fatal(constraint)
+
+ elif 'constraintErrorMessage' in properties:
+ r[kw.error] = properties.pop('constraintErrorMessage')
+
+ elif 'valueless' in properties:
+ properties.pop('valueless')
+ r[kw.valueless] = True
+
+ elif 'multi' in properties:
+ properties.pop('multi')
+ r[kw.multi] = True
+
+ elif 'hidden' in properties:
+ properties.pop('hidden')
+ r[kw.hidden] = True
+
+ elif 'completionHelp' in properties:
+ completionHelp = properties.pop('completionHelp')
+ r[kw.completion] = {}
+ while completionHelp:
+ if 'list' in completionHelp:
+ r[kw.completion][kw.list] = completionHelp.pop('list')
+ elif 'script' in completionHelp:
+ r[kw.completion][kw.script] = completionHelp.pop('script')
+ elif 'path' in completionHelp:
+ r[kw.completion][kw.path] = completionHelp.pop('path')
+ else:
+ _fatal(completionHelp.keys())
+
+ elif 'priority' in properties:
+ priority = int(properties.pop('priority'))
+ r[kw.priority] = priority
+ xml[kw.priorities].setdefault(priority, []).append(' '.join(inside))
+
+ else:
+ _fatal(properties.keys())
+
+ elif 'defaultValue' in keys:
+ default = conf.pop('defaultValue')
+ x = xml[kw.default]
+ for k in inside[:-1]:
+ x = x.setdefault(k,{})
+ x[inside[-1]] = '' if default is None else default
+
+ else:
+ _fatal(conf)
+
+ return r
+
+
+def xml(folder):
+ """
+ read all the xml in the folder
+ """
+ xml = definition.XML()
+ for fname in glob.glob(f'{folder}/*.xml.in'):
+ parsed = xmltodict.parse(_include(fname))
+ formated = _format_nodes([], parsed['interfaceDefinition'], xml)
+ _merge(xml[kw.tree], formated)
+ # fix the configuration root node for completion
+ # as we moved all the name "up" the chain to use them as index.
+ xml[kw.tree][kw.node] = kw.plainNode
+ # XXX: do the others
+ return xml
diff --git a/python/vyos/xml/test_xml.py b/python/vyos/xml/test_xml.py
new file mode 100644
index 000000000..ac0620d99
--- /dev/null
+++ b/python/vyos/xml/test_xml.py
@@ -0,0 +1,279 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import os
+import unittest
+from unittest import TestCase, mock
+
+from vyos.xml import load_configuration
+
+import sys
+
+
+class TestSearch(TestCase):
+ def setUp(self):
+ self.xml = load_configuration()
+
+ def test_(self):
+ last = self.xml.traverse("")
+ self.assertEqual(last, '')
+ self.assertEqual(self.xml.inside, [])
+ self.assertEqual(self.xml.options, ['protocols', 'service', 'system', 'firewall', 'interfaces', 'vpn', 'nat', 'vrf', 'high-availability'])
+ self.assertEqual(self.xml.filling, False)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, False)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, True)
+
+ def test_i(self):
+ last = self.xml.traverse("i")
+ self.assertEqual(last, 'i')
+ self.assertEqual(self.xml.inside, [])
+ self.assertEqual(self.xml.options, ['interfaces'])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, False)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, True)
+
+ def test_interfaces(self):
+ last = self.xml.traverse("interfaces")
+ self.assertEqual(last, '')
+ self.assertEqual(self.xml.inside, ['interfaces'])
+ self.assertEqual(self.xml.options, ['bonding', 'bridge', 'dummy', 'ethernet', 'geneve', 'l2tpv3', 'loopback', 'macsec', 'openvpn', 'pppoe', 'pseudo-ethernet', 'tunnel', 'vxlan', 'wireguard', 'wireless', 'wirelessmodem'])
+ self.assertEqual(self.xml.filling, False)
+ self.assertEqual(self.xml.word, '')
+ self.assertEqual(self.xml.check, False)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, True)
+
+ def test_interfaces_space(self):
+ last = self.xml.traverse("interfaces ")
+ self.assertEqual(last, '')
+ self.assertEqual(self.xml.inside, ['interfaces'])
+ self.assertEqual(self.xml.options, ['bonding', 'bridge', 'dummy', 'ethernet', 'geneve', 'l2tpv3', 'loopback', 'macsec', 'openvpn', 'pppoe', 'pseudo-ethernet', 'tunnel', 'vxlan', 'wireguard', 'wireless', 'wirelessmodem'])
+ self.assertEqual(self.xml.filling, False)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, False)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, True)
+
+ def test_interfaces_w(self):
+ last = self.xml.traverse("interfaces w")
+ self.assertEqual(last, 'w')
+ self.assertEqual(self.xml.inside, ['interfaces'])
+ self.assertEqual(self.xml.options, ['wireguard', 'wireless', 'wirelessmodem'])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, True)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, True)
+
+ def test_interfaces_ethernet(self):
+ last = self.xml.traverse("interfaces ethernet")
+ self.assertEqual(last, '')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, False)
+ self.assertEqual(self.xml.word, '')
+ self.assertEqual(self.xml.check, False)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_space(self):
+ last = self.xml.traverse("interfaces ethernet ")
+ self.assertEqual(last, '')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, False)
+ self.assertEqual(self.xml.word, '')
+ self.assertEqual(self.xml.check, False)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_e(self):
+ last = self.xml.traverse("interfaces ethernet e")
+ self.assertEqual(last, 'e')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, True)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_la(self):
+ last = self.xml.traverse("interfaces ethernet la")
+ self.assertEqual(last, 'la')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, True)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_lan0(self):
+ last = self.xml.traverse("interfaces ethernet lan0")
+ self.assertEqual(last, 'lan0')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, True)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_lan0_space(self):
+ last = self.xml.traverse("interfaces ethernet lan0 ")
+ self.assertEqual(last, '')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
+ self.assertEqual(len(self.xml.options), 19)
+ self.assertEqual(self.xml.filling, False)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, False)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, True)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_lan0_ad(self):
+ last = self.xml.traverse("interfaces ethernet lan0 ad")
+ self.assertEqual(last, 'ad')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
+ self.assertEqual(self.xml.options, ['address'])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, False)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_lan0_address(self):
+ last = self.xml.traverse("interfaces ethernet lan0 address")
+ self.assertEqual(last, '')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, False)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, False)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_lan0_address_space(self):
+ last = self.xml.traverse("interfaces ethernet lan0 address ")
+ self.assertEqual(last, '')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, False)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, False)
+ self.assertEqual(self.xml.final, False)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, False)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_lan0_address_space_11(self):
+ last = self.xml.traverse("interfaces ethernet lan0 address 1.1")
+ self.assertEqual(last, '1.1')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, True)
+ self.assertEqual(self.xml.final, True)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, True)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_lan0_address_space_1111_32(self):
+ last = self.xml.traverse("interfaces ethernet lan0 address 1.1.1.1/32")
+ self.assertEqual(last, '1.1.1.1/32')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, True)
+ self.assertEqual(self.xml.final, True)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, True)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_lan0_address_space_1111_32_space(self):
+ last = self.xml.traverse("interfaces ethernet lan0 address 1.1.1.1/32 ")
+ self.assertEqual(last, '1.1.1.1/32')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, True)
+ self.assertEqual(self.xml.final, True)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, True)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_lan0_address_space_1111_32_space_text(self):
+ last = self.xml.traverse("interfaces ethernet lan0 address 1.1.1.1/32 text")
+ self.assertEqual(last, '1.1.1.1/32 text')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, True)
+ self.assertEqual(self.xml.final, True)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, True)
+ self.assertEqual(self.xml.plain, False)
+
+ def test_interfaces_ethernet_lan0_address_space_1111_32_space_text_space(self):
+ last = self.xml.traverse("interfaces ethernet lan0 address 1.1.1.1/32 text ")
+ self.assertEqual(last, '1.1.1.1/32 text')
+ self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
+ self.assertEqual(self.xml.options, [])
+ self.assertEqual(self.xml.filling, True)
+ self.assertEqual(self.xml.word, last)
+ self.assertEqual(self.xml.check, True)
+ self.assertEqual(self.xml.final, True)
+ self.assertEqual(self.xml.extra, False)
+ self.assertEqual(self.xml.filled, True)
+ self.assertEqual(self.xml.plain, False)
+
+ # Need to add a check for a valuless leafNode \ No newline at end of file
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index ab3e073ae..c24c9a7ce 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -275,7 +275,7 @@ def apply(wg):
# peer pubkey
# setting up the wg interface
- w.config['private-key'] = c['pk']
+ w.config['private_key'] = c['pk']
for peer in wg['peer']:
# peer pubkey
@@ -300,13 +300,8 @@ def apply(wg):
if peer['persistent_keepalive']:
w.config['keepalive'] = peer['persistent_keepalive']
- # maybe move it into ifconfig.py
- # preshared-key - needs to be read from a file
if peer['psk']:
- psk_file = '/config/auth/wireguard/psk'
- with open(psk_file, 'w') as f:
- f.write(peer['psk'])
- w.config['psk'] = psk_file
+ w.config['psk'] = peer['psk']
w.update()
diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py
new file mode 100755
index 000000000..c5ac26806
--- /dev/null
+++ b/src/conf_mode/protocols_rip.py
@@ -0,0 +1,317 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+from sys import exit
+
+from vyos import ConfigError
+from vyos.config import Config
+from vyos.util import call
+from vyos.template import render
+
+from vyos import airbag
+airbag.enable()
+
+config_file = r'/tmp/ripd.frr'
+
+def get_config():
+ conf = Config()
+ base = ['protocols', 'rip']
+ rip_conf = {
+ 'rip_conf' : False,
+ 'default_distance' : [],
+ 'default_originate' : False,
+ 'old_rip' : {
+ 'default_metric' : [],
+ 'distribute' : {},
+ 'neighbors' : {},
+ 'networks' : {},
+ 'net_distance' : {},
+ 'passive_iface' : {},
+ 'redist' : {},
+ 'route' : {},
+ 'ifaces' : {},
+ 'timer_garbage' : 120,
+ 'timer_timeout' : 180,
+ 'timer_update' : 30
+ },
+ 'rip' : {
+ 'default_metric' : None,
+ 'distribute' : {},
+ 'neighbors' : {},
+ 'networks' : {},
+ 'net_distance' : {},
+ 'passive_iface' : {},
+ 'redist' : {},
+ 'route' : {},
+ 'ifaces' : {},
+ 'timer_garbage' : 120,
+ 'timer_timeout' : 180,
+ 'timer_update' : 30
+ }
+ }
+
+ if not (conf.exists(base) or conf.exists_effective(base)):
+ return None
+
+ if conf.exists(base):
+ rip_conf['rip_conf'] = True
+
+ conf.set_level(base)
+
+ # Get default distance
+ if conf.exists_effective('default-distance'):
+ rip_conf['old_default_distance'] = conf.return_effective_value('default-distance')
+
+ if conf.exists('default-distance'):
+ rip_conf['default_distance'] = conf.return_value('default-distance')
+
+ # Get default information originate (originate default route)
+ if conf.exists_effective('default-information originate'):
+ rip_conf['old_default_originate'] = True
+
+ if conf.exists('default-information originate'):
+ rip_conf['default_originate'] = True
+
+ # Get default-metric
+ if conf.exists_effective('default-metric'):
+ rip_conf['old_rip']['default_metric'] = conf.return_effective_value('default-metric')
+
+ if conf.exists('default-metric'):
+ rip_conf['rip']['default_metric'] = conf.return_value('default-metric')
+
+ # Get distribute list interface old_rip
+ for dist_iface in conf.list_effective_nodes('distribute-list interface'):
+ # Set level 'distribute-list interface ethX'
+ conf.set_level((str(base)) + ' distribute-list interface ' + dist_iface)
+ rip_conf['rip']['distribute'].update({
+ dist_iface : {
+ 'iface_access_list_in': conf.return_effective_value('access-list in'.format(dist_iface)),
+ 'iface_access_list_out': conf.return_effective_value('access-list out'.format(dist_iface)),
+ 'iface_prefix_list_in': conf.return_effective_value('prefix-list in'.format(dist_iface)),
+ 'iface_prefix_list_out': conf.return_effective_value('prefix-list out'.format(dist_iface))
+ }
+ })
+
+ # Access-list in old_rip
+ if conf.exists_effective('access-list in'.format(dist_iface)):
+ rip_conf['old_rip']['iface_access_list_in'] = conf.return_effective_value('access-list in'.format(dist_iface))
+ # Access-list out old_rip
+ if conf.exists_effective('access-list out'.format(dist_iface)):
+ rip_conf['old_rip']['iface_access_list_out'] = conf.return_effective_value('access-list out'.format(dist_iface))
+ # Prefix-list in old_rip
+ if conf.exists_effective('prefix-list in'.format(dist_iface)):
+ rip_conf['old_rip']['iface_prefix_list_in'] = conf.return_effective_value('prefix-list in'.format(dist_iface))
+ # Prefix-list out old_rip
+ if conf.exists_effective('prefix-list out'.format(dist_iface)):
+ rip_conf['old_rip']['iface_prefix_list_out'] = conf.return_effective_value('prefix-list out'.format(dist_iface))
+
+ conf.set_level(base)
+
+ # Get distribute list interface
+ for dist_iface in conf.list_nodes('distribute-list interface'):
+ # Set level 'distribute-list interface ethX'
+ conf.set_level((str(base)) + ' distribute-list interface ' + dist_iface)
+ rip_conf['rip']['distribute'].update({
+ dist_iface : {
+ 'iface_access_list_in': conf.return_value('access-list in'.format(dist_iface)),
+ 'iface_access_list_out': conf.return_value('access-list out'.format(dist_iface)),
+ 'iface_prefix_list_in': conf.return_value('prefix-list in'.format(dist_iface)),
+ 'iface_prefix_list_out': conf.return_value('prefix-list out'.format(dist_iface))
+ }
+ })
+
+ # Access-list in
+ if conf.exists('access-list in'.format(dist_iface)):
+ rip_conf['rip']['iface_access_list_in'] = conf.return_value('access-list in'.format(dist_iface))
+ # Access-list out
+ if conf.exists('access-list out'.format(dist_iface)):
+ rip_conf['rip']['iface_access_list_out'] = conf.return_value('access-list out'.format(dist_iface))
+ # Prefix-list in
+ if conf.exists('prefix-list in'.format(dist_iface)):
+ rip_conf['rip']['iface_prefix_list_in'] = conf.return_value('prefix-list in'.format(dist_iface))
+ # Prefix-list out
+ if conf.exists('prefix-list out'.format(dist_iface)):
+ rip_conf['rip']['iface_prefix_list_out'] = conf.return_value('prefix-list out'.format(dist_iface))
+
+ conf.set_level((str(base)) + ' distribute-list')
+
+ # Get distribute list, access-list in
+ if conf.exists_effective('access-list in'):
+ rip_conf['old_rip']['dist_acl_in'] = conf.return_effective_value('access-list in')
+
+ if conf.exists('access-list in'):
+ rip_conf['rip']['dist_acl_in'] = conf.return_value('access-list in')
+
+ # Get distribute list, access-list out
+ if conf.exists_effective('access-list out'):
+ rip_conf['old_rip']['dist_acl_out'] = conf.return_effective_value('access-list out')
+
+ if conf.exists('access-list out'):
+ rip_conf['rip']['dist_acl_out'] = conf.return_value('access-list out')
+
+ # Get ditstribute list, prefix-list in
+ if conf.exists_effective('prefix-list in'):
+ rip_conf['old_rip']['dist_prfx_in'] = conf.return_effective_value('prefix-list in')
+
+ if conf.exists('prefix-list in'):
+ rip_conf['rip']['dist_prfx_in'] = conf.return_value('prefix-list in')
+
+ # Get distribute list, prefix-list out
+ if conf.exists_effective('prefix-list out'):
+ rip_conf['old_rip']['dist_prfx_out'] = conf.return_effective_value('prefix-list out')
+
+ if conf.exists('prefix-list out'):
+ rip_conf['rip']['dist_prfx_out'] = conf.return_value('prefix-list out')
+
+ conf.set_level(base)
+
+ # Get network Interfaces
+ if conf.exists_effective('interface'):
+ rip_conf['old_rip']['ifaces'] = conf.return_effective_values('interface')
+
+ if conf.exists('interface'):
+ rip_conf['rip']['ifaces'] = conf.return_values('interface')
+
+ # Get neighbors
+ if conf.exists_effective('neighbor'):
+ rip_conf['old_rip']['neighbors'] = conf.return_effective_values('neighbor')
+
+ if conf.exists('neighbor'):
+ rip_conf['rip']['neighbors'] = conf.return_values('neighbor')
+
+ # Get networks
+ if conf.exists_effective('network'):
+ rip_conf['old_rip']['networks'] = conf.return_effective_values('network')
+
+ if conf.exists('network'):
+ rip_conf['rip']['networks'] = conf.return_values('network')
+
+ # Get network-distance old_rip
+ for net_dist in conf.list_effective_nodes('network-distance'):
+ rip_conf['old_rip']['net_distance'].update({
+ net_dist : {
+ 'access_list' : conf.return_effective_value('network-distance {0} access-list'.format(net_dist)),
+ 'distance' : conf.return_effective_value('network-distance {0} distance'.format(net_dist)),
+ }
+ })
+
+ # Get network-distance
+ for net_dist in conf.list_nodes('network-distance'):
+ rip_conf['rip']['net_distance'].update({
+ net_dist : {
+ 'access_list' : conf.return_value('network-distance {0} access-list'.format(net_dist)),
+ 'distance' : conf.return_value('network-distance {0} distance'.format(net_dist)),
+ }
+ })
+
+ # Get passive-interface
+ if conf.exists_effective('passive-interface'):
+ rip_conf['old_rip']['passive_iface'] = conf.return_effective_values('passive-interface')
+
+ if conf.exists('passive-interface'):
+ rip_conf['rip']['passive_iface'] = conf.return_values('passive-interface')
+
+ # Get redistribute for old_rip
+ for protocol in conf.list_effective_nodes('redistribute'):
+ rip_conf['old_rip']['redist'].update({
+ protocol : {
+ 'metric' : conf.return_effective_value('redistribute {0} metric'.format(protocol)),
+ 'route_map' : conf.return_effective_value('redistribute {0} route-map'.format(protocol)),
+ }
+ })
+
+ # Get redistribute
+ for protocol in conf.list_nodes('redistribute'):
+ rip_conf['rip']['redist'].update({
+ protocol : {
+ 'metric' : conf.return_value('redistribute {0} metric'.format(protocol)),
+ 'route_map' : conf.return_value('redistribute {0} route-map'.format(protocol)),
+ }
+ })
+
+ conf.set_level(base)
+
+ # Get route
+ if conf.exists_effective('route'):
+ rip_conf['old_rip']['route'] = conf.return_effective_values('route')
+
+ if conf.exists('route'):
+ rip_conf['rip']['route'] = conf.return_values('route')
+
+ # Get timers garbage
+ if conf.exists_effective('timers garbage-collection'):
+ rip_conf['old_rip']['timer_garbage'] = conf.return_effective_value('timers garbage-collection')
+
+ if conf.exists('timers garbage-collection'):
+ rip_conf['rip']['timer_garbage'] = conf.return_value('timers garbage-collection')
+
+ # Get timers timeout
+ if conf.exists_effective('timers timeout'):
+ rip_conf['old_rip']['timer_timeout'] = conf.return_effective_value('timers timeout')
+
+ if conf.exists('timers timeout'):
+ rip_conf['rip']['timer_timeout'] = conf.return_value('timers timeout')
+
+ # Get timers update
+ if conf.exists_effective('timers update'):
+ rip_conf['old_rip']['timer_update'] = conf.return_effective_value('timers update')
+
+ if conf.exists('timers update'):
+ rip_conf['rip']['timer_update'] = conf.return_value('timers update')
+
+ return rip_conf
+
+def verify(rip):
+ if rip is None:
+ return None
+
+ # Check for network. If network-distance acl is set and distance not set
+ for net in rip['rip']['net_distance']:
+ if not rip['rip']['net_distance'][net]['distance']:
+ raise ConfigError(f"Must specify distance for network {net}")
+
+def generate(rip):
+ if rip is None:
+ return None
+
+ render(config_file, 'frr/rip.frr.tmpl', rip)
+ return None
+
+def apply(rip):
+ if rip is None:
+ return None
+
+ if os.path.exists(config_file):
+ call("sudo vtysh -d ripd -f " + config_file)
+ os.remove(config_file)
+ else:
+ print("File {0} not found".format(config_file))
+
+
+ return None
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
+
diff --git a/src/conf_mode/service_console-server.py b/src/conf_mode/service_console-server.py
index 7f6967983..ace6b8ca4 100755
--- a/src/conf_mode/service_console-server.py
+++ b/src/conf_mode/service_console-server.py
@@ -22,17 +22,11 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.template import render
from vyos.util import call
+from vyos.xml import defaults
from vyos import ConfigError
config_file = r'/run/conserver/conserver.cf'
-# Default values are necessary until the implementation of T2588 is completed
-default_values = {
- 'data_bits': '8',
- 'parity': 'none',
- 'stop_bits': '1'
-}
-
def get_config():
conf = Config()
base = ['service', 'console-server']
@@ -52,6 +46,7 @@ def get_config():
# We have gathered the dict representation of the CLI, but there are default
# options which we need to update into the dictionary retrived.
+ default_values = defaults(base + ['device'])
for device in proxy['device'].keys():
tmp = dict_merge(default_values, proxy['device'][device])
proxy['device'][device] = tmp
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index e8777dcad..3149bbb2f 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -248,7 +248,7 @@ def get_config():
conf.set_level(base_path + ['authentication', 'radius', 'server', server])
if conf.exists(['fail-time']):
- radius['fail-time'] = conf.return_value(['fail-time'])
+ radius['fail_time'] = conf.return_value(['fail-time'])
if conf.exists(['port']):
radius['port'] = conf.return_value(['port'])
diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py
index 7e40be32a..88df2902e 100755
--- a/src/conf_mode/vpn_l2tp.py
+++ b/src/conf_mode/vpn_l2tp.py
@@ -157,7 +157,7 @@ def get_config():
conf.set_level(base_path + ['authentication', 'radius', 'server', server])
if conf.exists(['fail-time']):
- radius['fail-time'] = conf.return_value(['fail-time'])
+ radius['fail_time'] = conf.return_value(['fail-time'])
if conf.exists(['port']):
radius['port'] = conf.return_value(['port'])
diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py
index 5c8b53e1d..4536692d2 100755
--- a/src/conf_mode/vpn_pptp.py
+++ b/src/conf_mode/vpn_pptp.py
@@ -117,7 +117,7 @@ def get_config():
conf.set_level(base_path + ['authentication', 'radius', 'server', server])
if conf.exists(['fail-time']):
- radius['fail-time'] = conf.return_value(['fail-time'])
+ radius['fail_time'] = conf.return_value(['fail-time'])
if conf.exists(['port']):
radius['port'] = conf.return_value(['port'])
diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py
index e080ce0dd..4c4d8e403 100755
--- a/src/conf_mode/vpn_sstp.py
+++ b/src/conf_mode/vpn_sstp.py
@@ -124,7 +124,7 @@ def get_config():
conf.set_level(base_path + ['authentication', 'radius', 'server', server])
if conf.exists(['fail-time']):
- radius['fail-time'] = conf.return_value(['fail-time'])
+ radius['fail_time'] = conf.return_value(['fail-time'])
if conf.exists(['port']):
radius['port'] = conf.return_value(['port'])