diff options
Diffstat (limited to 'python')
-rw-r--r-- | python/vyos/configdict.py | 10 | ||||
-rw-r--r-- | python/vyos/ifconfig/bridge.py | 59 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 24 | ||||
-rw-r--r-- | python/vyos/ifconfig/tunnel.py | 203 | ||||
-rw-r--r-- | python/vyos/ifconfig/wireguard.py | 31 | ||||
-rw-r--r-- | python/vyos/ifconfig/wireless.py | 29 | ||||
-rw-r--r-- | python/vyos/limericks.py | 16 | ||||
-rw-r--r-- | python/vyos/template.py | 23 | ||||
-rw-r--r-- | python/vyos/xml/definition.py | 11 |
9 files changed, 175 insertions, 231 deletions
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index b14f96364..cdcd3f9ea 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -439,13 +439,13 @@ def get_accel_dict(config, base, chap_secrets): # options which we need to update into the dictionary retrived. default_values = defaults(base) - # defaults include RADIUS server specifics per TAG node which need to be - # added to individual RADIUS servers instead - so we can simply delete them + # T2665: defaults include RADIUS server specifics per TAG node which need to + # be added to individual RADIUS servers instead - so we can simply delete them if dict_search('authentication.radius.server', default_values): del default_values['authentication']['radius']['server'] - # defaults include static-ip address per TAG node which need to be added to - # individual local users instead - so we can simply delete them + # T2665: defaults include static-ip address per TAG node which need to be + # added to individual local users instead - so we can simply delete them if dict_search('authentication.local_users.username', default_values): del default_values['authentication']['local_users']['username'] @@ -471,6 +471,7 @@ def get_accel_dict(config, base, chap_secrets): # Add individual RADIUS server default values if dict_search('authentication.radius.server', dict): + # T2665 default_values = defaults(base + ['authentication', 'radius', 'server']) for server in dict_search('authentication.radius.server', dict): @@ -484,6 +485,7 @@ def get_accel_dict(config, base, chap_secrets): # Add individual local-user default values if dict_search('authentication.local_users.username', dict): + # T2665 default_values = defaults(base + ['authentication', 'local-users', 'username']) for username in dict_search('authentication.local_users.username', dict): diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py index 7c77e050a..d0d5da881 100644 --- a/python/vyos/ifconfig/bridge.py +++ b/python/vyos/ifconfig/bridge.py @@ -157,7 +157,7 @@ class BridgeIf(Interface): >>> BridgeIf('br0').set_stp(1) """ self.set_interface('stp', state) - + def set_vlan_filter(self, state): """ Set bridge Vlan Filter state. 0 -> Vlan Filter disabled, 1 -> Vlan Filter enabled @@ -192,12 +192,11 @@ class BridgeIf(Interface): >>> BridgeIf('br0').add_port('eth0') >>> BridgeIf('br0').add_port('eth1') """ - # Bridge port handling of wireless interfaces is done by hostapd. - if 'wlan' in interface: - return - - return self.set_interface('add_port', interface) - + try: + return self.set_interface('add_port', interface) + except: + from vyos import ConfigError + raise ConfigError('Error: Device does not allow enslaving to a bridge.') def del_port(self, interface): """ @@ -217,7 +216,7 @@ class BridgeIf(Interface): # call base class first super().update(config) - + ifname = config['ifname'] # Set ageing time @@ -255,7 +254,7 @@ class BridgeIf(Interface): if member in interfaces(): self.del_port(member) vlan_filter = 0 - + vlan_del = set() vlan_add = set() @@ -286,9 +285,9 @@ class BridgeIf(Interface): if 'priority' in interface_config: value = interface_config.get('priority') lower.set_path_priority(value) - + tmp = dict_search('native_vlan_removed', interface_config) - + for vlan_id in (tmp or []): cmd = f'bridge vlan del dev {interface} vid {vlan_id}' self._cmd(cmd) @@ -296,37 +295,43 @@ class BridgeIf(Interface): self._cmd(cmd) vlan_del.add(vlan_id) vlan_add.add(1) - + tmp = dict_search('allowed_vlan_removed', interface_config) - - + + for vlan_id in (tmp or []): cmd = f'bridge vlan del dev {interface} vid {vlan_id}' self._cmd(cmd) vlan_del.add(vlan_id) - + if 'native_vlan' in interface_config: vlan_filter = 1 cmd = f'bridge vlan del dev {interface} vid 1' self._cmd(cmd) - vlan_del.add(1) vlan_id = interface_config['native_vlan'] + if vlan_id != 1: + vlan_del.add(1) cmd = f'bridge vlan add dev {interface} vid {vlan_id} pvid untagged master' self._cmd(cmd) vlan_add.add(vlan_id) - else: - cmd = f'bridge vlan del dev {interface} vid 1' - self._cmd(cmd) - vlan_del.add(1) - + if 'allowed_vlan' in interface_config: vlan_filter = 1 + + if vlan_filter: + if 'native_vlan' not in interface_config: + cmd = f'bridge vlan del dev {interface} vid 1' + self._cmd(cmd) + + if 'allowed_vlan' in interface_config: for vlan in interface_config['allowed_vlan']: cmd = f'bridge vlan add dev {interface} vid {vlan} master' self._cmd(cmd) vlan_add.add(vlan) - - + + + + for vlan in vlan_del: if isinstance(vlan,str) and vlan.isnumeric(): if int(vlan) == 1: @@ -340,15 +345,15 @@ class BridgeIf(Interface): else: cmd = f'bridge vlan del dev {ifname} vid {vlan} self' self._cmd(cmd) - + for vlan in vlan_add: cmd = f'bridge vlan add dev {ifname} vid {vlan} self' self._cmd(cmd) - + # enable/disable Vlan Filter self.set_vlan_filter(vlan_filter) - - + + # Enable/Disable of an interface must always be done at the end of the # derived class to make use of the ref-counting set_admin_state() # function. We will only enable the interface if 'up' was called as diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 893623284..43cd7220a 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -890,9 +890,9 @@ class Interface(Control): self._config = dict_merge(tmp, self._config) render(options_file, 'dhcp-client/daemon-options.tmpl', - self._config, trim_blocks=True) + self._config) render(config_file, 'dhcp-client/ipv4.tmpl', - self._config, trim_blocks=True) + self._config) # 'up' check is mandatory b/c even if the interface is A/D, as soon as # the DHCP client is started the interface will be placed in u/u state. @@ -919,7 +919,7 @@ class Interface(Control): if enable and 'disable' not in self._config: render(config_file, 'dhcp-client/ipv6.tmpl', - self._config, trim_blocks=True) + self._config) # We must ignore any return codes. This is required to enable DHCPv6-PD # for interfaces which are yet not up and running. @@ -942,6 +942,15 @@ class Interface(Control): # method to apply()? self._config = config + # Change interface MAC address - re-set to real hardware address (hw-id) + # if custom mac is removed. Skip if bond member. + if 'is_bond_member' not in config: + mac = config.get('hw_id') + if 'mac' in config: + mac = config.get('mac') + if mac: + self.set_mac(mac) + # Update interface description self.set_alias(config.get('description', '')) @@ -1058,15 +1067,6 @@ class Interface(Control): for addr in tmp: self.del_ipv6_eui64_address(addr) - # Change interface MAC address - re-set to real hardware address (hw-id) - # if custom mac is removed. Skip if bond member. - if 'is_bond_member' not in config: - mac = config.get('hw_id') - if 'mac' in config: - mac = config.get('mac') - if mac: - self.set_mac(mac) - # Manage IPv6 link-local addresses tmp = dict_search('ipv6.address.no_default_link_local', config) # we must check explicitly for None type as if the key is set we will diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py index 4122d1a2f..00dc36420 100644 --- a/python/vyos/ifconfig/tunnel.py +++ b/python/vyos/ifconfig/tunnel.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2020 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,8 +18,11 @@ from copy import deepcopy +from netaddr import EUI +from netaddr import mac_unix_expanded +from random import getrandbits + from vyos.ifconfig.interface import Interface -from vyos.ifconfig.afi import IP4, IP6 from vyos.validate import assert_list def enable_to_on(value): @@ -42,7 +45,6 @@ class _Tunnel(Interface): **{ 'section': 'tunnel', 'prefixes': ['tun',], - 'bridgeable': False, }, } @@ -61,68 +63,73 @@ class _Tunnel(Interface): }, }} - # use for "options" and "updates" - # If an key is only in the options list, it can only be set at creation time - # the create comand will only be make using the key in options - - # If an option is in the updates list, it can be updated - # upon, the creation, all key not yet applied will be updated - - # multicast/allmulticast can not be part of the create command - - # options matrix: - # with ip = 4, we have multicast - # wiht ip = 6, nothing - # with tunnel = 4, we have tos, ttl, key - # with tunnel = 6, we have encaplimit, hoplimit, tclass, flowlabel - - # TODO: For multicast, it is allowed on IP6IP6 and Sit6RD - # TODO: to match vyatta but it should be checked for correctness - - updates = [] - - create = '' - change = '' - delete = '' - - ip = [] # AFI of the families which can be used in the tunnel - tunnel = 0 # invalid - need to be set by subclasses - def __init__(self, ifname, **config): self.config = deepcopy(config) if config else {} super().__init__(ifname, **config) def _create(self): + create = 'ip tunnel add {ifname} mode {type}' + # add " option-name option-name-value ..." for all options set options = " ".join(["{} {}".format(k, self.config[k]) for k in self.options if k in self.config and self.config[k]]) - self._cmd('{} {}'.format(self.create.format(**self.config), options)) + self._cmd('{} {}'.format(create.format(**self.config), options)) self.set_admin_state('down') - def _delete(self): - self.set_admin_state('down') - cmd = self.delete.format(**self.config) - return self._cmd(cmd) + def change_options(self): + change = 'ip tunnel cha {ifname} mode {type}' - def set_interface(self, option, value): - try: - return Interface.set_interface(self, option, value) - except Exception: - pass - - if value == '': - # remove the value so that it is not used - self.config.pop(option, '') - - if self.change: - self._cmd('{} {} {}'.format( - self.change.format(**self.config), option, value)) - return True + # add " option-name option-name-value ..." for all options set + options = " ".join(["{} {}".format(k, self.config[k]) + for k in self.options if k in self.config and self.config[k]]) + self._cmd('{} {}'.format(change.format(**self.config), options)) @classmethod def get_config(cls): return dict(zip(cls.options, ['']*len(cls.options))) + def get_mac(self): + """ + Get current interface MAC (Media Access Contrl) address used. + + NOTE: Tunnel interfaces have no "MAC" address by default. The content + of the 'address' file in /sys/class/net/device contains the + local-ip thus we generate a random MAC address instead + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').get_mac() + '00:50:ab:cd:ef:00' + """ + # we choose 40 random bytes for the MAC address, this gives + # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A') + tmp = EUI(getrandbits(48)).value + # set locally administered bit in MAC address + tmp |= 0xf20000000000 + # convert integer to "real" MAC address representation + mac = EUI(hex(tmp).split('x')[-1]) + # change dialect to use : as delimiter instead of - + mac.dialect = mac_unix_expanded + return str(mac) + + def update(self, config): + """ General helper function which works on a dictionary retrived by + get_config_dict(). It's main intention is to consolidate the scattered + interface setup code and provide a single point of entry when workin + on any interface. """ + + # call base class first + super().update(config) + + # Enable/Disable of an interface must always be done at the end of the + # derived class to make use of the ref-counting set_admin_state() + # function. We will only enable the interface if 'up' was called as + # often as 'down'. This is required by some interface implementations + # as certain parameters can only be changed when the interface is + # in admin-down state. This ensures the link does not flap during + # reconfiguration. + state = 'down' if 'disable' in config else 'up' + self.set_admin_state(state) class GREIf(_Tunnel): """ @@ -134,27 +141,8 @@ class GREIf(_Tunnel): https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre.c """ - definition = { - **_Tunnel.definition, - **{ - 'bridgeable': True, - }, - } - - ip = [IP4, IP6] - tunnel = IP4 - default = {'type': 'gre'} - required = ['local', ] # mGRE is a GRE without remote endpoint - options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key'] - updates = ['local', 'remote', 'dev', 'ttl', 'tos', - 'mtu', 'multicast', 'allmulticast'] - - create = 'ip tunnel add {ifname} mode {type}' - change = 'ip tunnel cha {ifname}' - delete = 'ip tunnel del {ifname}' - # GreTap also called GRE Bridge class GRETapIf(_Tunnel): @@ -173,19 +161,8 @@ class GRETapIf(_Tunnel): }, } - ip = [IP4, ] - tunnel = IP4 - default = {'type': 'gretap'} - required = ['local', ] - options = ['local', 'remote', 'ttl',] - updates = ['mtu', ] - - create = 'ip link add {ifname} type {type}' - change = '' - delete = 'ip link del {ifname}' - class IP6GREIf(_Tunnel): """ @@ -196,30 +173,9 @@ class IP6GREIf(_Tunnel): https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre6.c """ - ip = [IP4, IP6] - tunnel = IP6 - default = {'type': 'ip6gre'} - required = ['local', 'remote'] - options = ['local', 'remote', 'dev', 'encaplimit', 'hoplimit', 'tclass', 'flowlabel'] - updates = ['local', 'remote', 'dev', 'encaplimit', - 'hoplimit', 'tclass', 'flowlabel', - 'mtu', 'multicast', 'allmulticast'] - - create = 'ip tunnel add {ifname} mode {type}' - change = 'ip tunnel cha {ifname} mode {type}' - delete = 'ip tunnel del {ifname}' - - # using "ip tunnel change" without using "mode" causes errors - # sudo ip tunnel add tun100 mode ip6gre local ::1 remote 1::1 - # sudo ip tunnel cha tun100 hoplimit 100 - # *** stack smashing detected ** *: < unknown > terminated - # sudo ip tunnel cha tun100 local: : 2 - # Error: an IP address is expected rather than "::2" - # works if mode is explicit - class IPIPIf(_Tunnel): """ @@ -232,20 +188,8 @@ class IPIPIf(_Tunnel): # IPIP does not allow to pass multicast, unlike GRE # but the interface itself can be set with multicast - ip = [IP4,] - tunnel = IP4 - default = {'type': 'ipip'} - required = ['local', 'remote'] - options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key'] - updates = ['local', 'remote', 'dev', 'ttl', 'tos', - 'mtu', 'multicast', 'allmulticast'] - - create = 'ip tunnel add {ifname} mode {type}' - change = 'ip tunnel cha {ifname}' - delete = 'ip tunnel del {ifname}' - class IPIP6If(_Tunnel): """ @@ -255,22 +199,9 @@ class IPIP6If(_Tunnel): https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_ip6tnl.c """ - ip = [IP4,] - tunnel = IP6 - default = {'type': 'ipip6'} - required = ['local', 'remote'] - options = ['local', 'remote', 'dev', 'encaplimit', 'hoplimit', 'tclass', 'flowlabel'] - updates = ['local', 'remote', 'dev', 'encaplimit', - 'hoplimit', 'tclass', 'flowlabel', - 'mtu', 'multicast', 'allmulticast'] - - create = 'ip -6 tunnel add {ifname} mode {type}' - change = 'ip -6 tunnel cha {ifname}' - delete = 'ip -6 tunnel del {ifname}' - class IP6IP6If(IPIP6If): """ @@ -279,9 +210,6 @@ class IP6IP6If(IPIP6If): For more information please refer to: https://tools.ietf.org/html/rfc2473 """ - - ip = [IP6,] - default = {'type': 'ip6ip6'} @@ -293,20 +221,8 @@ class SitIf(_Tunnel): https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_iptnl.c """ - ip = [IP6, IP4] - tunnel = IP4 - default = {'type': 'sit'} - required = ['local', 'remote'] - options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key'] - updates = ['local', 'remote', 'dev', 'ttl', 'tos', - 'mtu', 'multicast', 'allmulticast'] - - create = 'ip tunnel add {ifname} mode {type}' - change = 'ip tunnel cha {ifname}' - delete = 'ip tunnel del {ifname}' - class Sit6RDIf(SitIf): """ @@ -314,15 +230,8 @@ class Sit6RDIf(SitIf): https://en.wikipedia.org/wiki/IPv6_rapid_deployment """ - - ip = [IP6,] - - required = ['remote', '6rd-prefix'] - # TODO: check if key can really be used with 6RD options = ['remote', 'ttl', 'tos', 'key', '6rd-prefix', '6rd-relay-prefix'] - updates = ['remote', 'ttl', 'tos', - 'mtu', 'multicast', 'allmulticast'] def _create(self): # do not call _Tunnel.create, building fully here diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py index da3bd4e89..9ee798ee8 100644 --- a/python/vyos/ifconfig/wireguard.py +++ b/python/vyos/ifconfig/wireguard.py @@ -17,6 +17,9 @@ import os import time from datetime import timedelta +from netaddr import EUI +from netaddr import mac_unix_expanded +from random import getrandbits from hurry.filesize import size from hurry.filesize import alternative @@ -162,13 +165,37 @@ class WireGuardIf(Interface): **{ 'section': 'wireguard', 'prefixes': ['wg', ], - 'bridgeable': True, + 'bridgeable': False, } } options = Interface.options + \ ['port', 'private_key', 'pubkey', 'psk', 'allowed_ips', 'fwmark', 'endpoint', 'keepalive'] + def get_mac(self): + """ + Get current interface MAC (Media Access Contrl) address used. + + NOTE: Tunnel interfaces have no "MAC" address by default. The content + of the 'address' file in /sys/class/net/device contains the + local-ip thus we generate a random MAC address instead + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').get_mac() + '00:50:ab:cd:ef:00' + """ + # we choose 40 random bytes for the MAC address, this gives + # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A') + tmp = EUI(getrandbits(48)).value + # set locally administered bit in MAC address + tmp |= 0xf20000000000 + # convert integer to "real" MAC address representation + mac = EUI(hex(tmp).split('x')[-1]) + # change dialect to use : as delimiter instead of - + mac.dialect = mac_unix_expanded + return str(mac) + def update(self, config): """ General helper function which works on a dictionary retrived by get_config_dict(). It's main intention is to consolidate the scattered @@ -221,7 +248,7 @@ class WireGuardIf(Interface): # Endpoint configuration is optional if {'address', 'port'} <= set(peer): - if is_ipv6(config['address']): + if is_ipv6(peer['address']): cmd += ' endpoint [{address}]:{port}' else: cmd += ' endpoint {address}:{port}' diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py index deca68bf0..37703d242 100644 --- a/python/vyos/ifconfig/wireless.py +++ b/python/vyos/ifconfig/wireless.py @@ -23,10 +23,8 @@ class WiFiIf(Interface): default = { 'type': 'wifi', - 'phy': '', - 'wds': 'off', + 'phy': 'phy0' } - definition = { **Interface.definition, **{ @@ -35,19 +33,12 @@ class WiFiIf(Interface): 'bridgeable': True, } } - options = Interface.options + \ ['phy', 'op_mode'] - _command_set = {**Interface._command_set, **{ - '4addr': { - 'shellcmd': 'iw dev {ifname} set 4addr {value}', - }, - }} - def _create(self): # all interfaces will be added in monitor mode - cmd = 'iw phy {phy} interface add {ifname} type monitor 4addr {wds}' \ + cmd = 'iw phy {phy} interface add {ifname} type monitor' \ .format(**self.config) self._cmd(cmd) @@ -59,20 +50,28 @@ class WiFiIf(Interface): .format(**self.config) self._cmd(cmd) - def set_4aadr_mode(self, state): - return self.set_interface('4addr', state) - def update(self, config): """ General helper function which works on a dictionary retrived by get_config_dict(). It's main intention is to consolidate the scattered interface setup code and provide a single point of entry when workin on any interface. """ - self.set_4aadr_mode('on' if 'wds' in config else 'off') + # We can not call add_to_bridge() until wpa_supplicant is running, thus + # we will remove the key from the config dict and react to this specal + # case in thie derived class. + # re-add ourselves to any bridge we might have fallen out of + bridge_member = '' + if 'is_bridge_member' in config: + bridge_member = config['is_bridge_member'] + del config['is_bridge_member'] # call base class first super().update(config) + # re-add ourselves to any bridge we might have fallen out of + if bridge_member: + self.add_to_bridge(bridge_member) + # Enable/Disable of an interface must always be done at the end of the # derived class to make use of the ref-counting set_admin_state() # function. We will only enable the interface if 'up' was called as diff --git a/python/vyos/limericks.py b/python/vyos/limericks.py index e03ccd32b..3c6744816 100644 --- a/python/vyos/limericks.py +++ b/python/vyos/limericks.py @@ -18,18 +18,18 @@ import random limericks = [ """ -A programmer who's name was Searle -Once wrote a long program in Perl. -Despite very few quirks -No one got how it works, -Not even the interpreter. +A programmer whose name was Searle +once wrote a long program in Perl. +Despite very few quirks, +no one got how it works. +Not even the interpreter perl(1). """, """ There was a young lady of Maine -Who set up IPsec VPN. +who set up IPsec VPN. Problems didn't arise -'til other vendors' device +till other vendors' device had to add she to that VPN. """, @@ -45,7 +45,7 @@ to get the damn build scripts to work. A network admin from Hong Kong knew MPPE cipher's not strong. But he was behind NAT, -so he put up we that, +so he put up with that, sad network admin from Hong Kong. """, diff --git a/python/vyos/template.py b/python/vyos/template.py index 58ba75972..b31f5bea2 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -18,24 +18,25 @@ import os from jinja2 import Environment from jinja2 import FileSystemLoader -from vyos.defaults import directories -from vyos.util import chmod, chown, makedir +from vyos.defaults import directories +from vyos.util import chmod +from vyos.util import chown +from vyos.util import makedir # Holds template filters registered via register_filter() _FILTERS = {} - -# reuse Environments with identical trim_blocks setting to improve performance +# reuse Environments with identical settings to improve performance @functools.lru_cache(maxsize=2) -def _get_environment(trim_blocks): +def _get_environment(): env = Environment( # Don't check if template files were modified upon re-rendering auto_reload=False, # Cache up to this number of templates for quick re-rendering cache_size=100, loader=FileSystemLoader(directories["templates"]), - trim_blocks=trim_blocks, + trim_blocks=True, ) env.filters.update(_FILTERS) return env @@ -62,12 +63,11 @@ def register_filter(name, func=None): return func -def render_to_string(template, content, trim_blocks=False, formater=None): +def render_to_string(template, content, formater=None): """Render a template from the template directory, raise on any errors. :param template: the path to the template relative to the template folder :param content: the dictionary of variables to put into rendering context - :param trim_blocks: controls the trim_blocks jinja2 feature :param formater: if given, it has to be a callable the rendered string is passed through @@ -78,7 +78,7 @@ def render_to_string(template, content, trim_blocks=False, formater=None): package is build (recovering the load time and overhead caused by having the file out of the code). """ - template = _get_environment(bool(trim_blocks)).get_template(template) + template = _get_environment().get_template(template) rendered = template.render(content) if formater is not None: rendered = formater(rendered) @@ -89,7 +89,6 @@ def render( destination, template, content, - trim_blocks=False, formater=None, permission=None, user=None, @@ -110,7 +109,7 @@ def render( # As we are opening the file with 'w', we are performing the rendering before # calling open() to not accidentally erase the file if rendering fails - rendered = render_to_string(template, content, trim_blocks, formater) + rendered = render_to_string(template, content, formater) # Write to file with open(destination, "w") as file: @@ -154,7 +153,7 @@ def is_ipv4(text): try: return ip_interface(text).version == 4 except: return False -@register_filter('ipv6') +@register_filter('is_ipv6') def is_ipv6(text): """ Filter IP address, return True on IPv6 address, False otherwise """ from ipaddress import ip_interface diff --git a/python/vyos/xml/definition.py b/python/vyos/xml/definition.py index 7831af4d2..f556c5ced 100644 --- a/python/vyos/xml/definition.py +++ b/python/vyos/xml/definition.py @@ -255,7 +255,7 @@ class XML(dict): if not flat: # _flatten will make this conversion - d = self.multi_to_list(lpath, d) + d = self.multi_to_list(lpath, d, defaults=True) r = {} for k in d: @@ -284,7 +284,7 @@ class XML(dict): return _flatten(lpath, len(lpath), d) - def multi_to_list(self, lpath, conf): + def multi_to_list(self, lpath, conf, defaults=False): r = {} for k in conf: # key mangling could also be done here @@ -293,11 +293,14 @@ class XML(dict): under = k fpath = lpath + [k] if isinstance(conf[k],dict): - r[under] = self.multi_to_list(fpath, conf[k]) + r[under] = self.multi_to_list(fpath, conf[k], defaults) continue value = conf[k] if self.is_multi(fpath) and not isinstance(value, list): - value = value.split(' ') + if not defaults: + value = [value] + else: + value = value.split(' ') r[under] = value return r |