diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 39b80ce08..43cd7220a 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -1,1233 +1,1233 @@
 # 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
 # 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, see <http://www.gnu.org/licenses/>.
 
 import os
 import re
 import json
 import jmespath
 
 from copy import deepcopy
 from glob import glob
 
 from ipaddress import IPv4Network
 from ipaddress import IPv6Address
 from ipaddress import IPv6Network
 from netifaces import ifaddresses
 # this is not the same as socket.AF_INET/INET6
 from netifaces import AF_INET
 from netifaces import AF_INET6
 
 from vyos import ConfigError
 from vyos.configdict import list_diff
 from vyos.configdict import dict_merge
 from vyos.template import render
 from vyos.util import mac2eui64
 from vyos.util import dict_search
 from vyos.template import is_ipv4
 from vyos.template import is_ipv6
 from vyos.validate import is_intf_addr_assigned
 from vyos.validate import assert_boolean
 from vyos.validate import assert_list
 from vyos.validate import assert_mac
 from vyos.validate import assert_mtu
 from vyos.validate import assert_positive
 from vyos.validate import assert_range
 
 from vyos.ifconfig.control import Control
 from vyos.ifconfig.vrrp import VRRP
 from vyos.ifconfig.operational import Operational
 from vyos.ifconfig import Section
 
 class Interface(Control):
     # This is the class which will be used to create
     # self.operational, it allows subclasses, such as
     # WireGuard to modify their display behaviour
     OperationalClass = Operational
 
     options = ['debug', 'create']
     required = []
     default = {
         'type': '',
         'debug': True,
         'create': True,
     }
     definition = {
         'section': '',
         'prefixes': [],
         'vlan': False,
         'bondable': False,
         'broadcast': False,
         'bridgeable':  False,
         'eternal': '',
     }
 
     _command_get = {
         'admin_state': {
             'shellcmd': 'ip -json link show dev {ifname}',
             'format': lambda j: 'up' if 'UP' in jmespath.search('[*].flags | [0]', json.loads(j)) else 'down',
         },
         'min_mtu': {
             'shellcmd': 'ip -json -detail link list dev {ifname}',
             'format': lambda j: jmespath.search('[*].min_mtu | [0]', json.loads(j)),
         },
         'max_mtu': {
             'shellcmd': 'ip -json -detail link list dev {ifname}',
             'format': lambda j: jmespath.search('[*].max_mtu | [0]', json.loads(j)),
         },
     }
 
     _command_set = {
         'admin_state': {
             'validate': lambda v: assert_list(v, ['up', 'down']),
             'shellcmd': 'ip link set dev {ifname} {value}',
         },
         'mac': {
             'validate': assert_mac,
             'shellcmd': 'ip link set dev {ifname} address {value}',
         },
         'vrf': {
             'convert': lambda v: f'master {v}' if v else 'nomaster',
             'shellcmd': 'ip link set dev {ifname} {value}',
         },
     }
 
     _sysfs_get = {
         'alias': {
             'location': '/sys/class/net/{ifname}/ifalias',
         },
         'mac': {
             'location': '/sys/class/net/{ifname}/address',
         },
         'mtu': {
             'location': '/sys/class/net/{ifname}/mtu',
         },
         'oper_state':{
             'location': '/sys/class/net/{ifname}/operstate',
         },
     }
 
     _sysfs_set = {
         'alias': {
             'convert': lambda name: name if name else '\0',
             'location': '/sys/class/net/{ifname}/ifalias',
         },
         'mtu': {
             'validate': assert_mtu,
             'location': '/sys/class/net/{ifname}/mtu',
         },
         'arp_cache_tmo': {
             'convert': lambda tmo: (int(tmo) * 1000),
             'location': '/proc/sys/net/ipv4/neigh/{ifname}/base_reachable_time_ms',
         },
         'arp_filter': {
             'validate': assert_boolean,
             'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_filter',
         },
         'arp_accept': {
             'validate': lambda arp: assert_range(arp,0,2),
             'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_accept',
         },
         'arp_announce': {
             'validate': assert_boolean,
             'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_announce',
         },
         'arp_ignore': {
             'validate': assert_boolean,
             'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_ignore',
         },
         'ipv4_forwarding': {
             'validate': assert_boolean,
             'location': '/proc/sys/net/ipv4/conf/{ifname}/forwarding',
         },
         'ipv6_accept_ra': {
             'validate': lambda ara: assert_range(ara,0,3),
             'location': '/proc/sys/net/ipv6/conf/{ifname}/accept_ra',
         },
         'ipv6_autoconf': {
             'validate': lambda aco: assert_range(aco,0,2),
             'location': '/proc/sys/net/ipv6/conf/{ifname}/autoconf',
         },
         'ipv6_forwarding': {
             'validate': lambda fwd: assert_range(fwd,0,2),
             'location': '/proc/sys/net/ipv6/conf/{ifname}/forwarding',
         },
         'ipv6_dad_transmits': {
             'validate': assert_positive,
             'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits',
         },
         'path_cost': {
             # XXX: we should set a maximum
             'validate': assert_positive,
             'location': '/sys/class/net/{ifname}/brport/path_cost',
             'errormsg': '{ifname} is not a bridge port member'
         },
         'path_priority': {
             # XXX: we should set a maximum
             'validate': assert_positive,
             'location': '/sys/class/net/{ifname}/brport/priority',
             'errormsg': '{ifname} is not a bridge port member'
         },
         'proxy_arp': {
             'validate': assert_boolean,
             'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp',
         },
         'proxy_arp_pvlan': {
             'validate': assert_boolean,
             'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp_pvlan',
         },
         # link_detect vs link_filter name weirdness
         'link_detect': {
             'validate': lambda link: assert_range(link,0,3),
             'location': '/proc/sys/net/ipv4/conf/{ifname}/link_filter',
         },
     }
 
     @classmethod
     def exists(cls, ifname):
         return os.path.exists(f'/sys/class/net/{ifname}')
 
     @classmethod
     def get_config(cls):
         """
         Some but not all interfaces require a configuration when they are added
         using iproute2. This method will provide the configuration dictionary
         used by this class.
         """
         return deepcopy(cls.default)
 
     def __init__(self, ifname, **kargs):
         """
         This is the base interface class which supports basic IP/MAC address
         operations as well as DHCP(v6). Other interface which represent e.g.
         and ethernet bridge are implemented as derived classes adding all
         additional functionality.
 
         For creation you will need to provide the interface type, otherwise
         the existing interface is used
 
         DEBUG:
         This class has embedded debugging (print) which can be enabled by
         creating the following file:
         vyos@vyos# touch /tmp/vyos.ifconfig.debug
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> i = Interface('eth0')
         """
 
         self.config = deepcopy(self.default)
         for k in self.options:
             if k in kargs:
                 self.config[k] = kargs[k]
 
         # make sure the ifname is the first argument and not from the dict
         self.config['ifname'] = ifname
         self._admin_state_down_cnt = 0
 
         # we must have updated config before initialising the Interface
         super().__init__(**kargs)
         self.ifname = ifname
 
         if not self.exists(ifname):
             # Any instance of Interface, such as Interface('eth0')
             # can be used safely to access the generic function in this class
             # as 'type' is unset, the class can not be created
             if not self.config['type']:
                 raise Exception(f'interface "{ifname}" not found')
 
             # Should an Instance of a child class (EthernetIf, DummyIf, ..)
             # be required, then create should be set to False to not accidentally create it.
             # In case a subclass does not define it, we use get to set the default to True
             if self.config.get('create',True):
                 for k in self.required:
                     if k not in kargs:
                         name = self.default['type']
                         raise ConfigError(f'missing required option {k} for {name} {ifname} creation')
 
                 self._create()
             # If we can not connect to the interface then let the caller know
             # as the class could not be correctly initialised
             else:
                 raise Exception('interface "{}" not found'.format(self.config['ifname']))
 
         # temporary list of assigned IP addresses
         self._addr = []
 
         self.operational = self.OperationalClass(ifname)
         self.vrrp = VRRP(ifname)
 
     def _create(self):
         cmd = 'ip link add dev {ifname} type {type}'.format(**self.config)
         self._cmd(cmd)
 
     def remove(self):
         """
         Remove interface from operating system. Removing the interface
         deconfigures all assigned IP addresses and clear possible DHCP(v6)
         client processes.
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> i = Interface('eth0')
         >>> i.remove()
         """
 
         # remove all assigned IP addresses from interface - this is a bit redundant
         # as the kernel will remove all addresses on interface deletion, but we
         # can not delete ALL interfaces, see below
         self.flush_addrs()
 
         # ---------------------------------------------------------------------
         # Any class can define an eternal regex in its definition
         # interface matching the regex will not be deleted
 
         eternal = self.definition['eternal']
         if not eternal:
             self._delete()
         elif not re.match(eternal, self.ifname):
             self._delete()
 
     def _delete(self):
         # NOTE (Improvement):
         # after interface removal no other commands should be allowed
         # to be called and instead should raise an Exception:
         cmd = 'ip link del dev {ifname}'.format(**self.config)
         return self._cmd(cmd)
 
     def get_min_mtu(self):
         """
         Get hardware minimum supported MTU
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').get_min_mtu()
         '60'
         """
         return int(self.get_interface('min_mtu'))
 
     def get_max_mtu(self):
         """
         Get hardware maximum supported MTU
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').get_max_mtu()
         '9000'
         """
         return int(self.get_interface('max_mtu'))
 
     def get_mtu(self):
         """
         Get/set interface mtu in bytes.
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').get_mtu()
         '1500'
         """
         return int(self.get_interface('mtu'))
 
     def set_mtu(self, mtu):
         """
         Get/set interface mtu in bytes.
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_mtu(1400)
         >>> Interface('eth0').get_mtu()
         '1400'
         """
         return self.set_interface('mtu', mtu)
 
     def get_mac(self):
         """
         Get current interface MAC (Media Access Contrl) address used.
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').get_mac()
         '00:50:ab:cd:ef:00'
         """
         return self.get_interface('mac')
 
     def set_mac(self, mac):
         """
         Set interface MAC (Media Access Contrl) address to given value.
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_mac('00:50:ab:cd:ef:01')
         """
 
         # If MAC is unchanged, bail out early
         if mac == self.get_mac():
             return None
 
         # MAC address can only be changed if interface is in 'down' state
         prev_state = self.get_admin_state()
         if prev_state == 'up':
             self.set_admin_state('down')
 
         self.set_interface('mac', mac)
 
         # Turn an interface to the 'up' state if it was changed to 'down' by this fucntion
         if prev_state == 'up':
             self.set_admin_state('up')
 
     def set_vrf(self, vrf=''):
         """
         Add/Remove interface from given VRF instance.
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_vrf('foo')
         >>> Interface('eth0').set_vrf()
         """
         self.set_interface('vrf', vrf)
 
     def set_arp_cache_tmo(self, tmo):
         """
         Set ARP cache timeout value in seconds. Internal Kernel representation
         is in milliseconds.
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_arp_cache_tmo(40)
         """
         return self.set_interface('arp_cache_tmo', tmo)
 
     def set_arp_filter(self, arp_filter):
         """
         Filter ARP requests
 
         1 - Allows you to have multiple network interfaces on the same
             subnet, and have the ARPs for each interface be answered
             based on whether or not the kernel would route a packet from
             the ARP'd IP out that interface (therefore you must use source
             based routing for this to work). In other words it allows control
             of which cards (usually 1) will respond to an arp request.
 
         0 - (default) The kernel can respond to arp requests with addresses
             from other interfaces. This may seem wrong but it usually makes
             sense, because it increases the chance of successful communication.
             IP addresses are owned by the complete host on Linux, not by
             particular interfaces. Only for more complex setups like load-
             balancing, does this behaviour cause problems.
         """
         return self.set_interface('arp_filter', arp_filter)
 
     def set_arp_accept(self, arp_accept):
         """
         Define behavior for gratuitous ARP frames who's IP is not
         already present in the ARP table:
         0 - don't create new entries in the ARP table
         1 - create new entries in the ARP table
 
         Both replies and requests type gratuitous arp will trigger the
         ARP table to be updated, if this setting is on.
 
         If the ARP table already contains the IP address of the
         gratuitous arp frame, the arp table will be updated regardless
         if this setting is on or off.
         """
         return self.set_interface('arp_accept', arp_accept)
 
     def set_arp_announce(self, arp_announce):
         """
         Define different restriction levels for announcing the local
         source IP address from IP packets in ARP requests sent on
         interface:
         0 - (default) Use any local address, configured on any interface
         1 - Try to avoid local addresses that are not in the target's
             subnet for this interface. This mode is useful when target
             hosts reachable via this interface require the source IP
             address in ARP requests to be part of their logical network
             configured on the receiving interface. When we generate the
             request we will check all our subnets that include the
             target IP and will preserve the source address if it is from
             such subnet.
 
         Increasing the restriction level gives more chance for
         receiving answer from the resolved target while decreasing
         the level announces more valid sender's information.
         """
         return self.set_interface('arp_announce', arp_announce)
 
     def set_arp_ignore(self, arp_ignore):
         """
         Define different modes for sending replies in response to received ARP
         requests that resolve local target IP addresses:
 
         0 - (default): reply for any local target IP address, configured
             on any interface
         1 - reply only if the target IP address is local address
             configured on the incoming interface
         """
         return self.set_interface('arp_ignore', arp_ignore)
 
     def set_ipv4_forwarding(self, forwarding):
         """
         Configure IPv4 forwarding.
         """
         return self.set_interface('ipv4_forwarding', forwarding)
 
     def set_ipv6_accept_ra(self, accept_ra):
         """
         Accept Router Advertisements; autoconfigure using them.
 
         It also determines whether or not to transmit Router Solicitations.
         If and only if the functional setting is to accept Router
         Advertisements, Router Solicitations will be transmitted.
 
         0 - Do not accept Router Advertisements.
         1 - (default) Accept Router Advertisements if forwarding is disabled.
         2 - Overrule forwarding behaviour. Accept Router Advertisements even if
             forwarding is enabled.
         """
         return self.set_interface('ipv6_accept_ra', accept_ra)
 
     def set_ipv6_autoconf(self, autoconf):
         """
         Autoconfigure addresses using Prefix Information in Router
         Advertisements.
         """
         return self.set_interface('ipv6_autoconf', autoconf)
 
     def add_ipv6_eui64_address(self, prefix):
         """
         Extended Unique Identifier (EUI), as per RFC2373, allows a host to
         assign itself a unique IPv6 address based on a given IPv6 prefix.
 
         Calculate the EUI64 from the interface's MAC, then assign it
         with the given prefix to the interface.
         """
         # T2863: only add a link-local IPv6 address if the interface returns
         # a MAC address. This is not the case on e.g. WireGuard interfaces.
         mac = self.get_mac()
         if mac:
             eui64 = mac2eui64(mac, prefix)
             prefixlen = prefix.split('/')[1]
             self.add_addr(f'{eui64}/{prefixlen}')
 
     def del_ipv6_eui64_address(self, prefix):
         """
         Delete the address based on the interface's MAC-based EUI64
         combined with the prefix address.
         """
         eui64 = mac2eui64(self.get_mac(), prefix)
         prefixlen = prefix.split('/')[1]
         self.del_addr(f'{eui64}/{prefixlen}')
 
 
     def set_ipv6_forwarding(self, forwarding):
         """
         Configure IPv6 interface-specific Host/Router behaviour.
 
         False:
 
         By default, Host behaviour is assumed.  This means:
 
         1. IsRouter flag is not set in Neighbour Advertisements.
         2. If accept_ra is TRUE (default), transmit Router
            Solicitations.
         3. If accept_ra is TRUE (default), accept Router
            Advertisements (and do autoconfiguration).
         4. If accept_redirects is TRUE (default), accept Redirects.
 
         True:
 
         If local forwarding is enabled, Router behaviour is assumed.
         This means exactly the reverse from the above:
 
         1. IsRouter flag is set in Neighbour Advertisements.
         2. Router Solicitations are not sent unless accept_ra is 2.
         3. Router Advertisements are ignored unless accept_ra is 2.
         4. Redirects are ignored.
         """
         return self.set_interface('ipv6_forwarding', forwarding)
 
     def set_ipv6_dad_messages(self, dad):
         """
         The amount of Duplicate Address Detection probes to send.
         Default: 1
         """
         return self.set_interface('ipv6_dad_transmits', dad)
 
     def set_link_detect(self, link_filter):
         """
         Configure kernel response in packets received on interfaces that are 'down'
 
         0 - Allow packets to be received for the address on this interface
             even if interface is disabled or no carrier.
 
         1 - Ignore packets received if interface associated with the incoming
             address is down.
 
         2 - Ignore packets received if interface associated with the incoming
             address is down or has no carrier.
 
         Default value is 0. Note that some distributions enable it in startup
         scripts.
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_link_detect(1)
         """
         return self.set_interface('link_detect', link_filter)
 
     def get_alias(self):
         """
         Get interface alias name used by e.g. SNMP
 
         Example:
         >>> Interface('eth0').get_alias()
         'interface description as set by user'
         """
         return self.get_interface('alias')
 
     def set_alias(self, ifalias=''):
         """
         Set interface alias name used by e.g. SNMP
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_alias('VyOS upstream interface')
 
         to clear alias e.g. delete it use:
 
         >>> Interface('eth0').set_ifalias('')
         """
         self.set_interface('alias', ifalias)
 
     def get_admin_state(self):
         """
         Get interface administrative state. Function will return 'up' or 'down'
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').get_admin_state()
         'up'
         """
         return self.get_interface('admin_state')
 
     def set_admin_state(self, state):
         """
         Set interface administrative state to be 'up' or 'down'
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_admin_state('down')
         >>> Interface('eth0').get_admin_state()
         'down'
         """
         if state == 'up':
             self._admin_state_down_cnt -= 1
             if self._admin_state_down_cnt < 1:
                 return self.set_interface('admin_state', state)
         else:
             self._admin_state_down_cnt += 1
             return self.set_interface('admin_state', state)
 
     def set_path_cost(self, cost):
         """
         Set interface path cost, only relevant for STP enabled interfaces
 
         Example:
 
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_path_cost(4)
         """
         self.set_interface('path_cost', cost)
 
     def set_path_priority(self, priority):
         """
         Set interface path priority, only relevant for STP enabled interfaces
 
         Example:
 
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_path_priority(4)
         """
         self.set_interface('path_priority', priority)
 
     def set_proxy_arp(self, enable):
         """
         Set per interface proxy ARP configuration
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_proxy_arp(1)
         """
         self.set_interface('proxy_arp', enable)
 
     def set_proxy_arp_pvlan(self, enable):
         """
         Private VLAN proxy arp.
         Basically allow proxy arp replies back to the same interface
         (from which the ARP request/solicitation was received).
 
         This is done to support (ethernet) switch features, like RFC
         3069, where the individual ports are NOT allowed to
         communicate with each other, but they are allowed to talk to
         the upstream router.  As described in RFC 3069, it is possible
         to allow these hosts to communicate through the upstream
         router by proxy_arp'ing. Don't need to be used together with
         proxy_arp.
 
         This technology is known by different names:
         In RFC 3069 it is called VLAN Aggregation.
         Cisco and Allied Telesyn call it Private VLAN.
         Hewlett-Packard call it Source-Port filtering or port-isolation.
         Ericsson call it MAC-Forced Forwarding (RFC Draft).
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').set_proxy_arp_pvlan(1)
         """
         self.set_interface('proxy_arp_pvlan', enable)
 
     def get_addr(self):
         """
         Retrieve assigned IPv4 and IPv6 addresses from given interface.
         This is done using the netifaces and ipaddress python modules.
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0').get_addrs()
         ['172.16.33.30/24', 'fe80::20c:29ff:fe11:a174/64']
         """
 
         ipv4 = []
         ipv6 = []
 
         if AF_INET in ifaddresses(self.config['ifname']).keys():
             for v4_addr in ifaddresses(self.config['ifname'])[AF_INET]:
                 # we need to manually assemble a list of IPv4 address/prefix
                 prefix = '/' + \
                     str(IPv4Network('0.0.0.0/' + v4_addr['netmask']).prefixlen)
                 ipv4.append(v4_addr['addr'] + prefix)
 
         if AF_INET6 in ifaddresses(self.config['ifname']).keys():
             for v6_addr in ifaddresses(self.config['ifname'])[AF_INET6]:
                 # Note that currently expanded netmasks are not supported. That means
                 # 2001:db00::0/24 is a valid argument while 2001:db00::0/ffff:ff00:: not.
                 # see https://docs.python.org/3/library/ipaddress.html
                 bits = bin(
                     int(v6_addr['netmask'].replace(':', ''), 16)).count('1')
                 prefix = '/' + str(bits)
 
                 # we alsoneed to remove the interface suffix on link local
                 # addresses
                 v6_addr['addr'] = v6_addr['addr'].split('%')[0]
                 ipv6.append(v6_addr['addr'] + prefix)
 
         return ipv4 + ipv6
 
     def add_addr(self, addr):
         """
         Add IP(v6) address to interface. Address is only added if it is not
         already assigned to that interface. Address format must be validated
         and compressed/normalized before calling this function.
 
         addr: can be an IPv4 address, IPv6 address, dhcp or dhcpv6!
               IPv4: add IPv4 address to interface
               IPv6: add IPv6 address to interface
               dhcp: start dhclient (IPv4) on interface
               dhcpv6: start WIDE DHCPv6 (IPv6) on interface
 
         Returns False if address is already assigned and wasn't re-added.
         Example:
         >>> from vyos.ifconfig import Interface
         >>> j = Interface('eth0')
         >>> j.add_addr('192.0.2.1/24')
         >>> j.add_addr('2001:db8::ffff/64')
         >>> j.get_addr()
         ['192.0.2.1/24', '2001:db8::ffff/64']
         """
         # XXX: normalize/compress with ipaddress if calling functions don't?
         # is subnet mask always passed, and in the same way?
 
         # do not add same address twice
         if addr in self._addr:
             return False
 
         addr_is_v4 = is_ipv4(addr)
 
         # we can't have both DHCP and static IPv4 addresses assigned
         for a in self._addr:
             if ( ( addr == 'dhcp' and a != 'dhcpv6' and is_ipv4(a) ) or
                     ( a == 'dhcp' and addr != 'dhcpv6' and addr_is_v4 ) ):
                 raise ConfigError((
                     "Can't configure both static IPv4 and DHCP address "
                     "on the same interface"))
 
         # add to interface
         if addr == 'dhcp':
             self.set_dhcp(True)
         elif addr == 'dhcpv6':
             self.set_dhcpv6(True)
         elif not is_intf_addr_assigned(self.ifname, addr):
             self._cmd(f'ip addr add "{addr}" '
                     f'{"brd + " if addr_is_v4 else ""}dev "{self.ifname}"')
         else:
             return False
 
         # add to cache
         self._addr.append(addr)
 
         return True
 
     def del_addr(self, addr):
         """
         Delete IP(v6) address from interface. Address is only deleted if it is
         assigned to that interface. Address format must be exactly the same as
         was used when adding the address.
 
         addr: can be an IPv4 address, IPv6 address, dhcp or dhcpv6!
               IPv4: delete IPv4 address from interface
               IPv6: delete IPv6 address from interface
               dhcp: stop dhclient (IPv4) on interface
               dhcpv6: stop dhclient (IPv6) on interface
 
         Returns False if address isn't already assigned and wasn't deleted.
         Example:
         >>> from vyos.ifconfig import Interface
         >>> j = Interface('eth0')
         >>> j.add_addr('2001:db8::ffff/64')
         >>> j.add_addr('192.0.2.1/24')
         >>> j.get_addr()
         ['192.0.2.1/24', '2001:db8::ffff/64']
         >>> j.del_addr('192.0.2.1/24')
         >>> j.get_addr()
         ['2001:db8::ffff/64']
         """
 
         # remove from interface
         if addr == 'dhcp':
             self.set_dhcp(False)
         elif addr == 'dhcpv6':
             self.set_dhcpv6(False)
         elif is_intf_addr_assigned(self.ifname, addr):
             self._cmd(f'ip addr del "{addr}" dev "{self.ifname}"')
         else:
             return False
 
         # remove from cache
         if addr in self._addr:
             self._addr.remove(addr)
 
         return True
 
     def flush_addrs(self):
         """
         Flush all addresses from an interface, including DHCP.
 
         Will raise an exception on error.
         """
         # stop DHCP(v6) if running
         self.set_dhcp(False)
         self.set_dhcpv6(False)
 
         # flush all addresses
         self._cmd(f'ip addr flush dev "{self.ifname}"')
 
     def add_to_bridge(self, bridge_dict):
         """
         Adds the interface to the bridge with the passed port config.
 
         Returns False if bridge doesn't exist.
         """
 
         # drop all interface addresses first
         self.flush_addrs()
 
         for bridge, bridge_config in bridge_dict.items():
             # add interface to bridge - use Section.klass to get BridgeIf class
             Section.klass(bridge)(bridge, create=True).add_port(self.ifname)
 
             # set bridge port path cost
             if 'cost' in bridge_config:
                 self.set_path_cost(bridge_config['cost'])
 
             # set bridge port path priority
             if 'priority' in bridge_config:
                 self.set_path_cost(bridge_config['priority'])
 
     def set_dhcp(self, enable):
         """
         Enable/Disable DHCP client on a given interface.
         """
         if enable not in [True, False]:
             raise ValueError()
 
         ifname = self.ifname
         config_base = r'/var/lib/dhcp/dhclient'
         config_file = f'{config_base}_{ifname}.conf'
         options_file = f'{config_base}_{ifname}.options'
         pid_file = f'{config_base}_{ifname}.pid'
         lease_file = f'{config_base}_{ifname}.leases'
 
         if enable and 'disable' not in self._config:
             if dict_search('dhcp_options.host_name', self._config) == None:
                 # read configured system hostname.
                 # maybe change to vyos hostd client ???
                 hostname = 'vyos'
                 with open('/etc/hostname', 'r') as f:
                     hostname = f.read().rstrip('\n')
                     tmp = {'dhcp_options' : { 'host_name' : hostname}}
                     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.
             # This is not what we intended to do when disabling an interface.
             return self._cmd(f'systemctl restart dhclient@{ifname}.service')
         else:
             self._cmd(f'systemctl stop dhclient@{ifname}.service')
 
             # cleanup old config files
             for file in [config_file, options_file, pid_file, lease_file]:
                 if os.path.isfile(file):
                     os.remove(file)
 
 
     def set_dhcpv6(self, enable):
         """
         Enable/Disable DHCPv6 client on a given interface.
         """
         if enable not in [True, False]:
             raise ValueError()
 
         ifname = self.ifname
         config_file = f'/run/dhcp6c/dhcp6c.{ifname}.conf'
 
         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.
             return self._popen(f'systemctl restart dhcp6c@{ifname}.service')
         else:
             self._popen(f'systemctl stop dhcp6c@{ifname}.service')
 
             if os.path.isfile(config_file):
                 os.remove(config_file)
 
 
     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. """
 
         # Cache the configuration - it will be reused inside e.g. DHCP handler
         # XXX: maybe pass the option via __init__ in the future and rename this
         # 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', ''))
 
         # Ignore link state changes
         value = '2' if 'disable_link_detect' in config else '1'
         self.set_link_detect(value)
 
         # Configure assigned interface IP addresses. No longer
         # configured addresses will be removed first
         new_addr = config.get('address', [])
 
         # always ensure DHCP client is stopped (when not configured explicitly)
         if 'dhcp' not in new_addr:
             self.del_addr('dhcp')
 
         # always ensure DHCPv6 client is stopped (when not configured as client
         # for IPv6 address or prefix delegation
         dhcpv6pd = dict_search('dhcpv6_options.pd', config)
         if 'dhcpv6' not in new_addr or dhcpv6pd == None:
             self.del_addr('dhcpv6')
 
         # determine IP addresses which are assigned to the interface and build a
         # list of addresses which are no longer in the dict so they can be removed
         cur_addr = self.get_addr()
         for addr in list_diff(cur_addr, new_addr):
             self.del_addr(addr)
 
         for addr in new_addr:
             self.add_addr(addr)
 
         # start DHCPv6 client when only PD was configured
         if dhcpv6pd != None:
             self.set_dhcpv6(True)
 
         # There are some items in the configuration which can only be applied
         # if this instance is not bound to a bridge. This should be checked
         # by the caller but better save then sorry!
         if not any(k in ['is_bond_member', 'is_bridge_member'] for k in config):
             # Bind interface to given VRF or unbind it if vrf node is not set.
             # unbinding will call 'ip link set dev eth0 nomaster' which will
             # also drop the interface out of a bridge or bond - thus this is
             # checked before
             self.set_vrf(config.get('vrf', ''))
 
         # Configure ARP cache timeout in milliseconds - has default value
         tmp = dict_search('ip.arp_cache_timeout', config)
         value = tmp if (tmp != None) else '30'
         self.set_arp_cache_tmo(value)
 
         # Configure ARP filter configuration
         tmp = dict_search('ip.disable_arp_filter', config)
         value = '0' if (tmp != None) else '1'
         self.set_arp_filter(value)
 
         # Configure ARP accept
         tmp = dict_search('ip.enable_arp_accept', config)
         value = '1' if (tmp != None) else '0'
         self.set_arp_accept(value)
 
         # Configure ARP announce
         tmp = dict_search('ip.enable_arp_announce', config)
         value = '1' if (tmp != None) else '0'
         self.set_arp_announce(value)
 
         # Configure ARP ignore
         tmp = dict_search('ip.enable_arp_ignore', config)
         value = '1' if (tmp != None) else '0'
         self.set_arp_ignore(value)
 
         # Enable proxy-arp on this interface
         tmp = dict_search('ip.enable_proxy_arp', config)
         value = '1' if (tmp != None) else '0'
         self.set_proxy_arp(value)
 
         # Enable private VLAN proxy ARP on this interface
         tmp = dict_search('ip.proxy_arp_pvlan', config)
         value = '1' if (tmp != None) else '0'
         self.set_proxy_arp_pvlan(value)
 
         # IPv4 forwarding
         tmp = dict_search('ip.disable_forwarding', config)
         value = '0' if (tmp != None) else '1'
         self.set_ipv4_forwarding(value)
 
         # IPv6 forwarding
         tmp = dict_search('ipv6.disable_forwarding', config)
         value = '0' if (tmp != None) else '1'
         self.set_ipv6_forwarding(value)
 
         # IPv6 router advertisements
         tmp = dict_search('ipv6.address.autoconf', config)
         value = '2' if (tmp != None) else '1'
         if 'dhcpv6' in new_addr:
             value = '2'
         self.set_ipv6_accept_ra(value)
 
         # IPv6 address autoconfiguration
         tmp = dict_search('ipv6.address.autoconf', config)
         value = '1' if (tmp != None) else '0'
         self.set_ipv6_autoconf(value)
 
         # IPv6 Duplicate Address Detection (DAD) tries
         tmp = dict_search('ipv6.dup_addr_detect_transmits', config)
         value = tmp if (tmp != None) else '1'
         self.set_ipv6_dad_messages(value)
 
         # MTU - Maximum Transfer Unit
         if 'mtu' in config:
             self.set_mtu(config.get('mtu'))
 
         # Delete old IPv6 EUI64 addresses before changing MAC
         tmp = dict_search('ipv6.address.eui64_old', config)
         if tmp:
             for addr in tmp:
                 self.del_ipv6_eui64_address(addr)
 
         # 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
         # get an empty dict (<class 'dict'>)
         if tmp is not None:
             self.del_ipv6_eui64_address('fe80::/64')
         else:
             self.add_ipv6_eui64_address('fe80::/64')
 
         # Add IPv6 EUI-based addresses
         tmp = dict_search('ipv6.address.eui64', config)
         if tmp:
             for addr in tmp:
                 self.add_ipv6_eui64_address(addr)
 
         # re-add ourselves to any bridge we might have fallen out of
         if 'is_bridge_member' in config:
             bridge_dict = config.get('is_bridge_member')
             self.add_to_bridge(bridge_dict)
 
         # remove no longer required 802.1ad (Q-in-Q VLANs)
         ifname = config['ifname']
         for vif_s_id in config.get('vif_s_remove', {}):
             vif_s_ifname = f'{ifname}.{vif_s_id}'
             VLANIf(vif_s_ifname).remove()
 
         # create/update 802.1ad (Q-in-Q VLANs)
         for vif_s_id, vif_s_config in config.get('vif_s', {}).items():
             tmp = deepcopy(VLANIf.get_config())
             tmp['protocol'] = vif_s_config['protocol']
             tmp['source_interface'] = ifname
             tmp['vlan_id'] = vif_s_id
 
             vif_s_ifname = f'{ifname}.{vif_s_id}'
             vif_s_config['ifname'] = vif_s_ifname
             s_vlan = VLANIf(vif_s_ifname, **tmp)
             s_vlan.update(vif_s_config)
 
             # remove no longer required client VLAN (vif-c)
             for vif_c_id in vif_s_config.get('vif_c_remove', {}):
                 vif_c_ifname = f'{vif_s_ifname}.{vif_c_id}'
                 VLANIf(vif_c_ifname).remove()
 
             # create/update client VLAN (vif-c) interface
             for vif_c_id, vif_c_config in vif_s_config.get('vif_c', {}).items():
                 tmp = deepcopy(VLANIf.get_config())
                 tmp['source_interface'] = vif_s_ifname
                 tmp['vlan_id'] = vif_c_id
 
                 vif_c_ifname = f'{vif_s_ifname}.{vif_c_id}'
                 vif_c_config['ifname'] = vif_c_ifname
                 c_vlan = VLANIf(vif_c_ifname, **tmp)
                 c_vlan.update(vif_c_config)
 
         # remove no longer required 802.1q VLAN interfaces
         for vif_id in config.get('vif_remove', {}):
             vif_ifname = f'{ifname}.{vif_id}'
             VLANIf(vif_ifname).remove()
 
         # create/update 802.1q VLAN interfaces
         for vif_id, vif_config in config.get('vif', {}).items():
             tmp = deepcopy(VLANIf.get_config())
             tmp['source_interface'] = ifname
             tmp['vlan_id'] = vif_id
 
             vif_ifname = f'{ifname}.{vif_id}'
             vif_config['ifname'] = vif_ifname
             vlan = VLANIf(vif_ifname, **tmp)
             vlan.update(vif_config)
 
 
 class VLANIf(Interface):
     """ Specific class which abstracts 802.1q and 802.1ad (Q-in-Q) VLAN interfaces """
     default = {
         'type': 'vlan',
         'source_interface': '',
         'vlan_id': '',
         'protocol': '',
         'ingress_qos': '',
         'egress_qos': '',
     }
 
     options = Interface.options + \
         ['source_interface', 'vlan_id', 'protocol', 'ingress_qos', 'egress_qos']
 
     def remove(self):
         """
         Remove interface from operating system. Removing the interface
         deconfigures all assigned IP addresses and clear possible DHCP(v6)
         client processes.
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> VLANIf('eth0.10').remove
         """
         # Do we have sub interfaces (VLANs)? As interfaces need to be deleted
         # "in order" starting from Q-in-Q we delete them first.
         for upper in glob(f'/sys/class/net/{self.ifname}/upper*'):
             # an upper interface could be named: upper_bond0.1000.1100, thus
             # we need top drop the upper_ prefix
             vif_c = os.path.basename(upper)
             vif_c = vif_c.replace('upper_', '')
             VLANIf(vif_c).remove()
 
         super().remove()
 
     def _create(self):
         # bail out early if interface already exists
         if self.exists(f'{self.ifname}'):
             return
 
         cmd = 'ip link add link {source_interface} name {ifname} type vlan id {vlan_id}'
         if self.config['protocol']:
             cmd += ' protocol {protocol}'
         if self.config['ingress_qos']:
             cmd += ' ingress-qos-map {ingress_qos}'
         if self.config['egress_qos']:
             cmd += ' egress-qos-map {egress_qos}'
 
         self._cmd(cmd.format(**self.config))
 
         # interface is always A/D down. It needs to be enabled explicitly
         self.set_admin_state('down')
 
     def set_admin_state(self, state):
         """
         Set interface administrative state to be 'up' or 'down'
 
         Example:
         >>> from vyos.ifconfig import Interface
         >>> Interface('eth0.10').set_admin_state('down')
         >>> Interface('eth0.10').get_admin_state()
         'down'
         """
         # A VLAN interface can only be placed in admin up state when
         # the lower interface is up, too
         lower_interface = glob(f'/sys/class/net/{self.ifname}/lower*/flags')[0]
         with open(lower_interface, 'r') as f:
             flags = f.read()
         # If parent is not up - bail out as we can not bring up the VLAN.
         # Flags are defined in kernel source include/uapi/linux/if.h
         if not int(flags, 16) & 1:
             return None
 
         return super().set_admin_state(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. """
 
         # 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)
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 7860b581f..b31f5bea2 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -1,217 +1,216 @@
 # 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
 # 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, see <http://www.gnu.org/licenses/>.
 
 import functools
 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
 
 
 def register_filter(name, func=None):
     """Register a function to be available as filter in templates under given name.
 
     It can also be used as a decorator, see below in this module for examples.
 
     :raise RuntimeError:
         when trying to register a filter after a template has been rendered already
     :raise ValueError: when trying to register a name which was taken already
     """
     if func is None:
         return functools.partial(register_filter, name)
     if _get_environment.cache_info().currsize:
         raise RuntimeError(
             "Filters can only be registered before rendering the first template"
         )
     if name in _FILTERS:
         raise ValueError(f"A filter with name {name!r} was registered already")
     _FILTERS[name] = func
     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
 
     The parsed template files are cached, so rendering the same file multiple times
     does not cause as too much overhead.
     If used everywhere, it could be changed to load the template from Python
     environment variables from an importable Python module generated when the Debian
     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)
     return rendered
 
 
 def render(
     destination,
     template,
     content,
-    trim_blocks=False,
     formater=None,
     permission=None,
     user=None,
     group=None,
 ):
     """Render a template from the template directory to a file, raise on any errors.
 
     :param destination: path to the file to save the rendered template in
     :param permission: permission bitmask to set for the output file
     :param user: user to own the output file
     :param group: group to own the output file
 
     All other parameters are as for :func:`render_to_string`.
     """
     # Create the directory if it does not exist
     folder = os.path.dirname(destination)
     makedir(folder, user, group)
 
     # 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:
         chmod(file.fileno(), permission)
         chown(file.fileno(), user, group)
         file.write(rendered)
 
 
 ##################################
 # Custom template filters follow #
 ##################################
 
 @register_filter('address_from_cidr')
 def address_from_cidr(text):
     """ Take an IPv4/IPv6 CIDR prefix and convert the network to an "address".
     Example:
     192.0.2.0/24 -> 192.0.2.0, 2001:db8::/48 -> 2001:db8::
     """
     from ipaddress import ip_network
     return str(ip_network(text).network_address)
 
 @register_filter('netmask_from_cidr')
 def netmask_from_cidr(text):
     """ Take CIDR prefix and convert the prefix length to a "subnet mask".
     Example:
       - 192.0.2.0/24 -> 255.255.255.0
       - 2001:db8::/48 -> ffff:ffff:ffff::
     """
     from ipaddress import ip_network
     return str(ip_network(text).netmask)
 
 @register_filter('is_ip')
 def is_ip(addr):
     """ Check addr if it is an IPv4 or IPv6 address """
     return is_ipv4(addr) or is_ipv6(addr)
 
 @register_filter('is_ipv4')
 def is_ipv4(text):
     """ Filter IP address, return True on IPv4 address, False otherwise """
     from ipaddress import ip_interface
     try: return ip_interface(text).version == 4
     except: return False
 
 @register_filter('is_ipv6')
 def is_ipv6(text):
     """ Filter IP address, return True on IPv6 address, False otherwise """
     from ipaddress import ip_interface
     try: return ip_interface(text).version == 6
     except: return False
 
 @register_filter('first_host_address')
 def first_host_address(text):
     """ Return first usable (host) IP address from given prefix.
     Example:
       - 10.0.0.0/24 -> 10.0.0.1
       - 2001:db8::/64 -> 2001:db8::
     """
     from ipaddress import ip_interface
     from ipaddress import IPv4Network
     from ipaddress import IPv6Network
 
     addr = ip_interface(text)
     if addr.version == 4:
         return str(addr.ip +1)
     return str(addr.ip)
 
 @register_filter('last_host_address')
 def last_host_address(text):
     """ Return first usable IP address from given prefix.
     Example:
       - 10.0.0.0/24 -> 10.0.0.254
       - 2001:db8::/64 -> 2001:db8::ffff:ffff:ffff:ffff
     """
     from ipaddress import ip_interface
     from ipaddress import IPv4Network
     from ipaddress import IPv6Network
 
     addr = ip_interface(text)
     if addr.version == 4:
         return str(IPv4Network(addr).broadcast_address - 1)
 
     return str(IPv6Network(addr).broadcast_address)
 
 @register_filter('inc_ip')
 def inc_ip(address, increment):
     """ Increment given IP address by 'increment'
 
     Example (inc by 2):
       - 10.0.0.0/24 -> 10.0.0.2
       - 2001:db8::/64 -> 2001:db8::2
     """
     from ipaddress import ip_interface
     return str(ip_interface(address).ip + int(increment))
 
 @register_filter('dec_ip')
 def dec_ip(address, decrement):
     """ Decrement given IP address by 'decrement'
 
     Example (inc by 2):
       - 10.0.0.0/24 -> 10.0.0.2
       - 2001:db8::/64 -> 2001:db8::2
     """
     from ipaddress import ip_interface
     return str(ip_interface(address).ip - int(decrement))
diff --git a/src/conf_mode/bcast_relay.py b/src/conf_mode/bcast_relay.py
index 78daeb6be..d93a2a8f4 100755
--- a/src/conf_mode/bcast_relay.py
+++ b/src/conf_mode/bcast_relay.py
@@ -1,111 +1,111 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2017-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 glob import glob
 from netifaces import interfaces
 from sys import exit
 
 from vyos.config import Config
 from vyos.util import call
 from vyos.template import render
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 config_file_base = r'/etc/default/udp-broadcast-relay'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['service', 'broadcast-relay']
 
     relay = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
     return relay
 
 def verify(relay):
     if not relay or 'disabled' in relay:
         return None
 
     for instance, config in relay.get('id', {}).items():
         # we don't have to check this instance when it's disabled
         if 'disabled' in config:
             continue
 
         # we certainly require a UDP port to listen to
         if 'port' not in config:
             raise ConfigError(f'Port number mandatory for udp broadcast relay "{instance}"')
 
         # if only oone interface is given it's a string -> move to list
         if isinstance(config.get('interface', []), str):
             config['interface'] = [ config['interface'] ]
         # Relaying data without two interface is kinda senseless ...
         if len(config.get('interface', [])) < 2:
             raise ConfigError('At least two interfaces are required for udp broadcast relay "{instance}"')
 
         for interface in config.get('interface', []):
             if interface not in interfaces():
                 raise ConfigError('Interface "{interface}" does not exist!')
 
     return None
 
 def generate(relay):
     if not relay or 'disabled' in relay:
         return None
 
     for config in glob(config_file_base + '*'):
         os.remove(config)
 
     for instance, config in relay.get('id').items():
         # we don't have to check this instance when it's disabled
         if 'disabled' in config:
             continue
 
         config['instance'] = instance
         render(config_file_base + instance, 'bcast-relay/udp-broadcast-relay.tmpl',
-               config, trim_blocks=True)
+               config)
 
     return None
 
 def apply(relay):
     # first stop all running services
     call('systemctl stop udp-broadcast-relay@*.service')
 
     if not relay or 'disable' in relay:
         return None
 
     # start only required service instances
     for instance, config in relay.get('id').items():
         # we don't have to check this instance when it's disabled
         if 'disabled' in config:
             continue
 
         call(f'systemctl start udp-broadcast-relay@{instance}.service')
 
     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/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index ef52cbfd3..c44e6c974 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -1,185 +1,185 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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.config import Config
 from vyos.configdict import dict_merge
 from vyos.hostsd_client import Client as hostsd_client
 from vyos.template import render
 from vyos.template import is_ipv6
 from vyos.util import call
 from vyos.util import chown
 from vyos.util import dict_search
 from vyos.xml import defaults
 
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 pdns_rec_user = pdns_rec_group = 'pdns'
 pdns_rec_run_dir = '/run/powerdns'
 pdns_rec_lua_conf_file = f'{pdns_rec_run_dir}/recursor.conf.lua'
 pdns_rec_hostsd_lua_conf_file = f'{pdns_rec_run_dir}/recursor.vyos-hostsd.conf.lua'
 pdns_rec_hostsd_zones_file = f'{pdns_rec_run_dir}/recursor.forward-zones.conf'
 pdns_rec_config_file = f'{pdns_rec_run_dir}/recursor.conf'
 
 hostsd_tag = 'static'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['service', 'dns', 'forwarding']
     if not conf.exists(base):
         return None
 
     dns = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
     # 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)
     dns = dict_merge(default_values, dns)
 
     # some additions to the default dictionary
     if 'system' in dns:
         base_nameservers = ['system', 'name-server']
         if conf.exists(base_nameservers):
             dns.update({'system_name_server': conf.return_values(base_nameservers)})
 
         base_nameservers_dhcp = ['system', 'name-servers-dhcp']
         if conf.exists(base_nameservers_dhcp):
             dns.update({'system_name_server_dhcp': conf.return_values(base_nameservers_dhcp)})
 
     # Split the source_address property into separate IPv4 and IPv6 lists
     # NOTE: In future versions of pdns-recursor (> 4.4.0), this logic can be removed
     # as both IPv4 and IPv6 addresses can be specified in a single setting.
     source_address_v4 = []
     source_address_v6 = []
 
     for source_address in dns['source_address']:
         if is_ipv6(source_address):
             source_address_v6.append(source_address)
         else:
             source_address_v4.append(source_address)
 
     dns.update({'source_address_v4': source_address_v4})
     dns.update({'source_address_v6': source_address_v6})
 
     return dns
 
 def verify(dns):
     # bail out early - looks like removal from running config
     if not dns:
         return None
 
     if 'listen_address' not in dns:
         raise ConfigError('DNS forwarding requires a listen-address')
 
     if 'allow_from' not in dns:
         raise ConfigError('DNS forwarding requires an allow-from network')
 
     # we can not use dict_search() when testing for domain servers
     # as a domain will contains dot's which is out dictionary delimiter.
     if 'domain' in dns:
         for domain in dns['domain']:
             if 'server' not in dns['domain'][domain]:
                 raise ConfigError(f'No server configured for domain {domain}!')
 
     if 'system' in dns:
         if not ('system_name_server' in dns or 'system_name_server_dhcp' in dns):
             print("Warning: No 'system name-server' or 'system " \
                   "name-servers-dhcp' configured")
 
     return None
 
 def generate(dns):
     # bail out early - looks like removal from running config
     if not dns:
         return None
 
     render(pdns_rec_config_file, 'dns-forwarding/recursor.conf.tmpl',
-            dns, trim_blocks=True, user=pdns_rec_user, group=pdns_rec_group)
+            dns, user=pdns_rec_user, group=pdns_rec_group)
 
     render(pdns_rec_lua_conf_file, 'dns-forwarding/recursor.conf.lua.tmpl',
-            dns, trim_blocks=True, user=pdns_rec_user, group=pdns_rec_group)
+            dns, user=pdns_rec_user, group=pdns_rec_group)
 
     # if vyos-hostsd didn't create its files yet, create them (empty)
     for file in [pdns_rec_hostsd_lua_conf_file, pdns_rec_hostsd_zones_file]:
         with open(file, 'a'):
             pass
         chown(file, user=pdns_rec_user, group=pdns_rec_group)
 
     return None
 
 def apply(dns):
     if not dns:
         # DNS forwarding is removed in the commit
         call('systemctl stop pdns-recursor.service')
 
         if os.path.isfile(pdns_rec_config_file):
             os.unlink(pdns_rec_config_file)
     else:
         ### first apply vyos-hostsd config
         hc = hostsd_client()
 
         # add static nameservers to hostsd so they can be joined with other
         # sources
         hc.delete_name_servers([hostsd_tag])
         if 'name_server' in dns:
             hc.add_name_servers({hostsd_tag: dns['name_server']})
 
         # delete all nameserver tags
         hc.delete_name_server_tags_recursor(hc.get_name_server_tags_recursor())
 
         ## add nameserver tags - the order determines the nameserver order!
         # our own tag (static)
         hc.add_name_server_tags_recursor([hostsd_tag])
 
         if 'system' in dns:
             hc.add_name_server_tags_recursor(['system'])
         else:
             hc.delete_name_server_tags_recursor(['system'])
 
         # add dhcp nameserver tags for configured interfaces
         if 'system_name_server_dhcp' in dns:
             for interface in dns['system_name_server_dhcp']:
                 hc.add_name_server_tags_recursor(['dhcp-' + interface,
                                                   'dhcpv6-' + interface ])
 
         # hostsd will generate the forward-zones file
         # the list and keys() are required as get returns a dict, not list
         hc.delete_forward_zones(list(hc.get_forward_zones().keys()))
         if 'domain' in dns:
             hc.add_forward_zones(dns['domain'])
 
         # call hostsd to generate forward-zones and its lua-config-file
         hc.apply()
 
         ### finally (re)start pdns-recursor
         call('systemctl restart pdns-recursor.service')
 
 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/dynamic_dns.py b/src/conf_mode/dynamic_dns.py
index 93e995b78..6d39c6644 100755
--- a/src/conf_mode/dynamic_dns.py
+++ b/src/conf_mode/dynamic_dns.py
@@ -1,155 +1,157 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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.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
 from vyos import airbag
 airbag.enable()
 
 config_file = r'/run/ddclient/ddclient.conf'
 
 # Mapping of service name to service protocol
 default_service_protocol = {
     'afraid': 'freedns',
     'changeip': 'changeip',
     'cloudflare': 'cloudflare',
     'dnspark': 'dnspark',
     'dslreports': 'dslreports1',
     'dyndns': 'dyndns2',
     'easydns': 'easydns',
     'namecheap': 'namecheap',
     'noip': 'noip',
     'sitelutions': 'sitelutions',
     'zoneedit': 'zoneedit1'
 }
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     base_level = ['service', 'dns', 'dynamic']
     if not conf.exists(base_level):
         return None
 
     dyndns = conf.get_config_dict(base_level, key_mangling=('-', '_'), get_first_key=True)
 
     # We have gathered the dict representation of the CLI, but there are default
     # options which we need to update into the dictionary retrived.
     for interface in dyndns['interface']:
         if 'service' in dyndns['interface'][interface]:
             # 'Autodetect' protocol used by DynDNS service
             for service in dyndns['interface'][interface]['service']:
                 if service in default_service_protocol:
                     dyndns['interface'][interface]['service'][service].update(
                         {'protocol' : default_service_protocol.get(service)})
                 else:
                     dyndns['interface'][interface]['service'][service].update(
                         {'custom': ''})
 
         if 'rfc2136' in dyndns['interface'][interface]:
             default_values = defaults(base_level + ['interface', 'rfc2136'])
             for rfc2136 in dyndns['interface'][interface]['rfc2136']:
                 dyndns['interface'][interface]['rfc2136'][rfc2136] = dict_merge(
                     default_values, dyndns['interface'][interface]['rfc2136'][rfc2136])
 
     return dyndns
 
 def verify(dyndns):
     # bail out early - looks like removal from running config
     if not dyndns:
         return None
 
     # A 'node' corresponds to an interface
     if 'interface' not in dyndns:
         return None
 
     for interface in dyndns['interface']:
         # RFC2136 - configuration validation
         if 'rfc2136' in dyndns['interface'][interface]:
             for rfc2136, config in dyndns['interface'][interface]['rfc2136'].items():
 
                 for tmp in ['record', 'zone', 'server', 'key']:
                     if tmp not in config:
                         raise ConfigError(f'"{tmp}" required for rfc2136 based '
                                           f'DynDNS service on "{interface}"')
 
                 if not os.path.isfile(config['key']):
                     raise ConfigError(f'"key"-file not found for rfc2136 based '
                                       f'DynDNS service on "{interface}"')
 
         # DynDNS service provider - configuration validation
         if 'service' in dyndns['interface'][interface]:
             for service, config in dyndns['interface'][interface]['service'].items():
                 error_msg = f'required for DynDNS service "{service}" on "{interface}"'
                 if 'host_name' not in config:
                     raise ConfigError(f'"host-name" {error_msg}')
 
                 if 'login' not in config:
                     raise ConfigError(f'"login" (username) {error_msg}')
 
                 if 'password' not in config:
                     raise ConfigError(f'"password" {error_msg}')
 
                 if 'zone' in config:
                     if service != 'cloudflare':
                         raise ConfigError(f'"zone" option only supported with CloudFlare')
 
                 if 'custom' in config:
                     if 'protocol' not in config:
                         raise ConfigError(f'"protocol" {error_msg}')
 
                     if 'server' not in config:
                         raise ConfigError(f'"server" {error_msg}')
 
     return None
 
 def generate(dyndns):
     # bail out early - looks like removal from running config
     if not dyndns:
         return None
 
-    render(config_file, 'dynamic-dns/ddclient.conf.tmpl', dyndns, trim_blocks=True, permission=0o600)
+    render(config_file, 'dynamic-dns/ddclient.conf.tmpl', dyndns,
+           permission=0o600)
+
     return None
 
 def apply(dyndns):
     if not dyndns:
         call('systemctl stop ddclient.service')
         if os.path.exists(config_file):
             os.unlink(config_file)
     else:
         call('systemctl restart ddclient.service')
 
     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/https.py b/src/conf_mode/https.py
index de228f0f8..a6e2d9c8c 100755
--- a/src/conf_mode/https.py
+++ b/src/conf_mode/https.py
@@ -1,180 +1,180 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-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 sys
 
 from copy import deepcopy
 
 import vyos.defaults
 import vyos.certbot_util
 
 from vyos.config import Config
 from vyos import ConfigError
 from vyos.util import call
 from vyos.template import render
 
 from vyos import airbag
 airbag.enable()
 
 config_file = '/etc/nginx/sites-available/default'
 certbot_dir = vyos.defaults.directories['certbot']
 
 # https config needs to coordinate several subsystems: api, certbot,
 # self-signed certificate, as well as the virtual hosts defined within the
 # https config definition itself. Consequently, one needs a general dict,
 # encompassing the https and other configs, and a list of such virtual hosts
 # (server blocks in nginx terminology) to pass to the jinja2 template.
 default_server_block = {
     'id'        : '',
     'address'   : '*',
     'port'      : '443',
     'name'      : ['_'],
     'api'       : {},
     'vyos_cert' : {},
     'certbot'   : False
 }
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     if not conf.exists('service https'):
         return None
 
     server_block_list = []
     https_dict = conf.get_config_dict('service https', get_first_key=True)
 
     # organize by vhosts
 
     vhost_dict = https_dict.get('virtual-host', {})
 
     if not vhost_dict:
         # no specified virtual hosts (server blocks); use default
         server_block_list.append(default_server_block)
     else:
         for vhost in list(vhost_dict):
             server_block = deepcopy(default_server_block)
             server_block['id'] = vhost
             data = vhost_dict.get(vhost, {})
             server_block['address'] = data.get('listen-address', '*')
             server_block['port'] = data.get('listen-port', '443')
             name = data.get('server-name', ['_'])
             server_block['name'] = name
             server_block_list.append(server_block)
 
     # get certificate data
 
     cert_dict = https_dict.get('certificates', {})
 
         # self-signed certificate
 
     vyos_cert_data = {}
     if 'system-generated-certificate' in list(cert_dict):
         vyos_cert_data = vyos.defaults.vyos_cert_data
     if vyos_cert_data:
         for block in server_block_list:
             block['vyos_cert'] = vyos_cert_data
 
         # letsencrypt certificate using certbot
 
     certbot = False
     cert_domains = cert_dict.get('certbot', {}).get('domain-name', [])
     if cert_domains:
         certbot = True
         for domain in cert_domains:
             sub_list = vyos.certbot_util.choose_server_block(server_block_list,
                                                              domain)
             if sub_list:
                 for sb in sub_list:
                     sb['certbot'] = True
                     sb['certbot_dir'] = certbot_dir
                     # certbot organizes certificates by first domain
                     sb['certbot_domain_dir'] = cert_domains[0]
 
     # get api data
 
     api_set = False
     api_data = {}
     if 'api' in list(https_dict):
         api_set = True
         api_data = vyos.defaults.api_data
     api_settings = https_dict.get('api', {})
     if api_settings:
         port = api_settings.get('port', '')
         if port:
             api_data['port'] = port
         vhosts = https_dict.get('api-restrict', {}).get('virtual-host', [])
         if vhosts:
             api_data['vhost'] = vhosts[:]
 
     if api_data:
         vhost_list = api_data.get('vhost', [])
         if not vhost_list:
             for block in server_block_list:
                 block['api'] = api_data
         else:
             for block in server_block_list:
                 if block['id'] in vhost_list:
                     block['api'] = api_data
 
     # return dict for use in template
 
     https = {'server_block_list' : server_block_list,
              'api_set': api_set,
              'certbot': certbot}
 
     return https
 
 def verify(https):
     if https is None:
         return None
 
     if https['certbot']:
         for sb in https['server_block_list']:
             if sb['certbot']:
                 return None
         raise ConfigError("At least one 'virtual-host <id> server-name' "
                           "matching the 'certbot domain-name' is required.")
     return None
 
 def generate(https):
     if https is None:
         return None
 
     if 'server_block_list' not in https or not https['server_block_list']:
         https['server_block_list'] = [default_server_block]
 
-    render(config_file, 'https/nginx.default.tmpl', https, trim_blocks=True)
+    render(config_file, 'https/nginx.default.tmpl', https)
 
     return None
 
 def apply(https):
     if https is not None:
         call('systemctl restart nginx.service')
     else:
         call('systemctl stop nginx.service')
 
 if __name__ == '__main__':
     try:
         c = get_config()
         verify(c)
         generate(c)
         apply(c)
     except ConfigError as e:
         print(e)
         sys.exit(1)
diff --git a/src/conf_mode/igmp_proxy.py b/src/conf_mode/igmp_proxy.py
index 90f3f30a8..fb030c9f3 100755
--- a/src/conf_mode/igmp_proxy.py
+++ b/src/conf_mode/igmp_proxy.py
@@ -1,122 +1,121 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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 netifaces import interfaces
 
 from vyos.config import Config
 from vyos.configdict import dict_merge
 from vyos.template import render
 from vyos.util import call
 from vyos.util import dict_search
 from vyos.xml import defaults
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 config_file = r'/etc/igmpproxy.conf'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     base = ['protocols', 'igmp-proxy']
     igmp_proxy = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
 
     if 'interface' in igmp_proxy:
         # T2665: we must add the tagNode defaults individually until this is
         # moved to the base class
         default_values = defaults(base + ['interface'])
         for interface in igmp_proxy['interface']:
             igmp_proxy['interface'][interface] = dict_merge(default_values,
                 igmp_proxy['interface'][interface])
 
 
     if conf.exists(['protocols', 'igmp']):
         igmp_proxy.update({'igmp_configured': ''})
 
     if conf.exists(['protocols', 'pim']):
         igmp_proxy.update({'pim_configured': ''})
 
     return igmp_proxy
 
 def verify(igmp_proxy):
     # bail out early - looks like removal from running config
     if not igmp_proxy or 'disable' in igmp_proxy:
         return None
 
     if 'igmp_configured' in igmp_proxy or 'pim_configured' in igmp_proxy:
         raise ConfigError('Can not configure both IGMP proxy and PIM '\
                           'at the same time')
 
     # at least two interfaces are required, one upstream and one downstream
     if 'interface' not in igmp_proxy or len(igmp_proxy['interface']) < 2:
         raise ConfigError('Must define exactly one upstream and at least one ' \
                           'downstream interface!')
 
     upstream = 0
     for interface, config in igmp_proxy['interface'].items():
         if interface not in interfaces():
             raise ConfigError(f'Interface "{interface}" does not exist')
         if dict_search('role', config) == 'upstream':
             upstream += 1
 
     if upstream == 0:
         raise ConfigError('At least 1 upstream interface is required!')
     elif upstream > 1:
         raise ConfigError('Only 1 upstream interface allowed!')
 
     return None
 
 def generate(igmp_proxy):
     # bail out early - looks like removal from running config
     if not igmp_proxy:
         return None
 
     # bail out early - service is disabled, but inform user
     if 'disable' in igmp_proxy:
         print('WARNING: IGMP Proxy will be deactivated because it is disabled')
         return None
 
-    render(config_file, 'igmp-proxy/igmpproxy.conf.tmpl', igmp_proxy,
-           trim_blocks=True)
+    render(config_file, 'igmp-proxy/igmpproxy.conf.tmpl', igmp_proxy)
 
     return None
 
 def apply(igmp_proxy):
     if not igmp_proxy or 'disable' in igmp_proxy:
          # IGMP Proxy support is removed in the commit
          call('systemctl stop igmpproxy.service')
          if os.path.exists(config_file):
              os.unlink(config_file)
     else:
         call('systemctl restart igmpproxy.service')
 
     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/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 0e661c84b..25920f893 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -1,541 +1,540 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-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 re
 
 from sys import exit
 from ipaddress import IPv4Address
 from ipaddress import IPv4Network
 from ipaddress import IPv6Address
 from ipaddress import IPv6Network
 from ipaddress import summarize_address_range
 from netifaces import interfaces
 from shutil import rmtree
 
 from vyos.config import Config
 from vyos.configdict import get_interface_dict
 from vyos.configverify import verify_vrf
 from vyos.configverify import verify_bridge_delete
 from vyos.configverify import verify_diffie_hellman_length
 from vyos.ifconfig import VTunIf
 from vyos.template import render
 from vyos.template import is_ipv4
 from vyos.template import is_ipv6
 from vyos.util import call
 from vyos.util import chown
 from vyos.util import chmod_600
 from vyos.util import dict_search
 from vyos.validate import is_addr_assigned
 
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 user = 'openvpn'
 group = 'openvpn'
 
 cfg_file = '/run/openvpn/{ifname}.conf'
 
 def checkCertHeader(header, filename):
     """
     Verify if filename contains specified header.
     Returns True if match is found, False if no match or file is not found
     """
     if not os.path.isfile(filename):
         return False
 
     with open(filename, 'r') as f:
         for line in f:
             if re.match(header, line):
                 return True
 
     return False
 
 def get_config(config=None):
     """
     Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
     interface name will be added or a deleted flag
     """
     if config:
         conf = config
     else:
         conf = Config()
     base = ['interfaces', 'openvpn']
     openvpn = get_interface_dict(conf, base)
 
     openvpn['auth_user_pass_file'] = '/run/openvpn/{ifname}.pw'.format(**openvpn)
     openvpn['daemon_user'] = user
     openvpn['daemon_group'] = group
 
     return openvpn
 
 def verify(openvpn):
     if 'deleted' in openvpn:
         verify_bridge_delete(openvpn)
         return None
 
     if 'mode' not in openvpn:
         raise ConfigError('Must specify OpenVPN operation mode!')
 
     # Check if we have disabled ncp and at the same time specified ncp-ciphers
     if 'encryption' in openvpn:
         if {'disable_ncp', 'ncp_ciphers'} <= set(openvpn.get('encryption')):
             raise ConfigError('Can not specify both "encryption disable-ncp" '\
                               'and "encryption ncp-ciphers"')
 
     #
     # OpenVPN client mode - VERIFY
     #
     if openvpn['mode'] == 'client':
         if 'local_port' in openvpn:
             raise ConfigError('Cannot specify "local-port" in client mode')
 
         if 'local_host' in openvpn:
             raise ConfigError('Cannot specify "local-host" in client mode')
 
         if 'remote_host' not in openvpn:
             raise ConfigError('Must specify "remote-host" in client mode')
 
         if openvpn['protocol'] == 'tcp-passive':
             raise ConfigError('Protocol "tcp-passive" is not valid in client mode')
 
         if dict_search('tls.dh_file', openvpn):
             raise ConfigError('Cannot specify "tls dh-file" in client mode')
 
     #
     # OpenVPN site-to-site - VERIFY
     #
     elif openvpn['mode'] == 'site-to-site':
         if 'local_address' not in openvpn and 'is_bridge_member' not in openvpn:
             raise ConfigError('Must specify "local-address" or add interface to bridge')
 
         if len([addr for addr in openvpn['local_address'] if is_ipv4(addr)]) > 1:
             raise ConfigError('Only one IPv4 local-address can be specified')
 
         if len([addr for addr in openvpn['local_address'] if is_ipv6(addr)]) > 1:
             raise ConfigError('Only one IPv6 local-address can be specified')
 
         if openvpn['device_type'] == 'tun':
             if 'remote_address' not in openvpn:
                 raise ConfigError('Must specify "remote-address"')
 
         if 'remote_address' in openvpn:
             if len([addr for addr in openvpn['remote_address'] if is_ipv4(addr)]) > 1:
                 raise ConfigError('Only one IPv4 remote-address can be specified')
 
             if len([addr for addr in openvpn['remote_address'] if is_ipv6(addr)]) > 1:
                 raise ConfigError('Only one IPv6 remote-address can be specified')
 
             if not 'local_address' in openvpn:
                 raise ConfigError('"remote-address" requires "local-address"')
 
             v4loAddr = [addr for addr in openvpn['local_address'] if is_ipv4(addr)]
             v4remAddr = [addr for addr in openvpn['remote_address'] if is_ipv4(addr)]
             if v4loAddr and not v4remAddr:
                 raise ConfigError('IPv4 "local-address" requires IPv4 "remote-address"')
             elif v4remAddr and not v4loAddr:
                 raise ConfigError('IPv4 "remote-address" requires IPv4 "local-address"')
 
             v6remAddr = [addr for addr in openvpn['remote_address'] if is_ipv6(addr)]
             v6loAddr = [addr for addr in openvpn['local_address'] if is_ipv6(addr)]
             if v6loAddr and not v6remAddr:
                 raise ConfigError('IPv6 "local-address" requires IPv6 "remote-address"')
             elif v6remAddr and not v6loAddr:
                 raise ConfigError('IPv6 "remote-address" requires IPv6 "local-address"')
 
             if (v4loAddr == v4remAddr) or (v6remAddr == v4remAddr):
                 raise ConfigError('"local-address" and "remote-address" cannot be the same')
 
             if dict_search('local_host', openvpn) in dict_search('local_address', openvpn):
                 raise ConfigError('"local-address" cannot be the same as "local-host"')
 
             if dict_search('remote_host', openvpn) in dict_search('remote_address', openvpn):
                 raise ConfigError('"remote-address" and "remote-host" can not be the same')
 
         if openvpn['device_type'] == 'tap':
             # we can only have one local_address, this is ensured above
             v4addr = None
             for laddr in openvpn['local_address']:
                 if is_ipv4(laddr):
                     v4addr = laddr
                     break
 
             if v4addr in openvpn['local_address'] and 'subnet_mask' not in openvpn['local_address'][v4addr]:
                 raise ConfigError('Must specify IPv4 "subnet-mask" for local-address')
 
         if dict_search('encryption.ncp_ciphers', openvpn):
             raise ConfigError('NCP ciphers can only be used in client or server mode')
 
     else:
         # checks for client-server or site-to-site bridged
         if 'local_address' in openvpn or 'remote_address' in openvpn:
             raise ConfigError('Cannot specify "local-address" or "remote-address" ' \
                               'in client/server or bridge mode')
 
     #
     # OpenVPN server mode - VERIFY
     #
     if openvpn['mode'] == 'server':
         if openvpn['protocol'] == 'tcp-active':
             raise ConfigError('Protocol "tcp-active" is not valid in server mode')
 
         if 'remote_port' in openvpn:
             raise ConfigError('Cannot specify "remote-port" in server mode')
 
         if 'remote_host' in openvpn:
             raise ConfigError('Cannot specify "remote-host" in server mode')
 
         if 'tls' in openvpn:
             if 'dh_file' not in openvpn['tls']:
                 if 'key_file' in openvpn['tls'] and not checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls']['key_file']):
                     raise ConfigError('Must specify "tls dh-file" when not using EC keys in server mode')
 
         tmp = dict_search('server.subnet', openvpn)
         if tmp:
             v4_subnets = len([subnet for subnet in tmp if is_ipv4(subnet)])
             v6_subnets = len([subnet for subnet in tmp if is_ipv6(subnet)])
             if v4_subnets > 1:
                 raise ConfigError('Cannot specify more than 1 IPv4 server subnet')
             if v6_subnets > 1:
                 raise ConfigError('Cannot specify more than 1 IPv6 server subnet')
 
             if v6_subnets > 0 and v4_subnets == 0:
                 raise ConfigError('IPv6 server requires an IPv4 server subnet')
 
             for subnet in tmp:
                 if is_ipv4(subnet):
                     subnet = IPv4Network(subnet)
 
                     if openvpn['device_type'] == 'tun' and subnet.prefixlen > 29:
                         raise ConfigError('Server subnets smaller than /29 with device type "tun" are not supported')
                     elif openvpn['device_type'] == 'tap' and subnet.prefixlen > 30:
                         raise ConfigError('Server subnets smaller than /30 with device type "tap" are not supported')
 
                     for client in (dict_search('client', openvpn) or []):
                         if client['ip'] and not IPv4Address(client['ip'][0]) in subnet:
                             raise ConfigError(f'Client "{client["name"]}" IP {client["ip"][0]} not in server subnet {subnet}')
 
         else:
             if 'is_bridge_member' not in openvpn:
                 raise ConfigError('Must specify "server subnet" or add interface to bridge in server mode')
 
 
         for client in (dict_search('client', openvpn) or []):
             if len(client['ip']) > 1 or len(client['ipv6_ip']) > 1:
                 raise ConfigError(f'Server client "{client["name"]}": cannot specify more than 1 IPv4 and 1 IPv6 IP')
 
         if dict_search('server.client_ip_pool', openvpn):
             if not (dict_search('server.client_ip_pool.start', openvpn) and dict_search('server.client_ip_pool.stop', openvpn)):
                 raise ConfigError('Server client-ip-pool requires both start and stop addresses')
             else:
                 v4PoolStart = IPv4Address(dict_search('server.client_ip_pool.start', openvpn))
                 v4PoolStop = IPv4Address(dict_search('server.client_ip_pool.stop', openvpn))
                 if v4PoolStart > v4PoolStop:
                     raise ConfigError(f'Server client-ip-pool start address {v4PoolStart} is larger than stop address {v4PoolStop}')
 
                 v4PoolSize = int(v4PoolStop) - int(v4PoolStart)
                 if v4PoolSize >= 65536:
                     raise ConfigError(f'Server client-ip-pool is too large [{v4PoolStart} -> {v4PoolStop} = {v4PoolSize}], maximum is 65536 addresses.')
 
                 v4PoolNets = list(summarize_address_range(v4PoolStart, v4PoolStop))
                 for client in (dict_search('client', openvpn) or []):
                     if client['ip']:
                         for v4PoolNet in v4PoolNets:
                             if IPv4Address(client['ip'][0]) in v4PoolNet:
                                 print(f'Warning: Client "{client["name"]}" IP {client["ip"][0]} is in server IP pool, it is not reserved for this client.')
 
         for subnet in (dict_search('server.subnet', openvpn) or []):
             if is_ipv6(subnet):
                 tmp = dict_search('client_ipv6_pool.base', openvpn)
                 if tmp:
                     if not dict_search('server.client_ip_pool', openvpn):
                         raise ConfigError('IPv6 server pool requires an IPv4 server pool')
 
                     if int(tmp.split('/')[1]) >= 112:
                         raise ConfigError('IPv6 server pool must be larger than /112')
 
                     #
                     # todo - weird logic
                     #
                     v6PoolStart = IPv6Address(tmp)
                     v6PoolStop = IPv6Network((v6PoolStart, openvpn['server_ipv6_pool_prefixlen']), strict=False)[-1] # don't remove the parentheses, it's a 2-tuple
                     v6PoolSize = int(v6PoolStop) - int(v6PoolStart) if int(openvpn['server_ipv6_pool_prefixlen']) > 96 else 65536
                     if v6PoolSize < v4PoolSize:
                         raise ConfigError(f'IPv6 server pool must be at least as large as the IPv4 pool (current sizes: IPv6={v6PoolSize} IPv4={v4PoolSize})')
 
                     v6PoolNets = list(summarize_address_range(v6PoolStart, v6PoolStop))
                     for client in (dict_search('client', openvpn) or []):
                         if client['ipv6_ip']:
                             for v6PoolNet in v6PoolNets:
                                 if IPv6Address(client['ipv6_ip'][0]) in v6PoolNet:
                                     print(f'Warning: Client "{client["name"]}" IP {client["ipv6_ip"][0]} is in server IP pool, it is not reserved for this client.')
 
             else:
                 for route in (dict_search('server.push_route', openvpn) or []):
                     if is_ipv6(route):
                         raise ConfigError('IPv6 push-route requires an IPv6 server subnet')
 
             #for client in openvpn ['client']:
             #    if client['ipv6_ip']:
             #        raise ConfigError(f'Server client "{client["name"]}" IPv6 IP requires an IPv6 server subnet')
             #    if client['ipv6_push_route']:
             #        raise ConfigError(f'Server client "{client["name"]} IPv6 push-route requires an IPv6 server subnet"')
             #    if client['ipv6_subnet']:
             #        raise ConfigError(f'Server client "{client["name"]} IPv6 subnet requires an IPv6 server subnet"')
 
     else:
         # checks for both client and site-to-site go here
         if dict_search('server.reject_unconfigured_clients', openvpn):
             raise ConfigError('Option reject-unconfigured-clients only supported in server mode')
 
         if 'replace_default_route' in openvpn and 'remote_host' not in openvpn:
             raise ConfigError('Cannot set "replace-default-route" without "remote-host"')
 
     #
     # OpenVPN common verification section
     # not depending on any operation mode
     #
 
     # verify specified IP address is present on any interface on this system
     if 'local_host' in openvpn:
         if not is_addr_assigned(openvpn['local_host']):
             raise ConfigError('local-host IP address "{local_host}" not assigned' \
                               ' to any interface'.format(**openvpn))
 
     # TCP active
     if openvpn['protocol'] == 'tcp-active':
         if 'local_port' in openvpn:
             raise ConfigError('Cannot specify "local-port" with "tcp-active"')
 
         if 'remote_host' not in openvpn:
             raise ConfigError('Must specify "remote-host" with "tcp-active"')
 
     # shared secret and TLS
     if not ('shared_secret_key_file' in openvpn or 'tls' in openvpn):
         raise ConfigError('Must specify one of "shared-secret-key-file" and "tls"')
 
     if {'shared_secret_key_file', 'tls'} <= set(openvpn):
         raise ConfigError('Can only specify one of "shared-secret-key-file" and "tls"')
 
     if openvpn['mode'] in ['client', 'server']:
         if 'tls' not in openvpn:
             raise ConfigError('Must specify "tls" for server and client mode')
 
     #
     # TLS/encryption
     #
     if 'shared_secret_key_file' in openvpn:
         if dict_search('encryption.cipher', openvpn) in ['aes128gcm', 'aes192gcm', 'aes256gcm']:
             raise ConfigError('GCM encryption with shared-secret-key-file not supported')
 
         file = dict_search('shared_secret_key_file', openvpn)
         if file and not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', file):
             raise ConfigError(f'Specified shared-secret-key-file "{file}" is not valid')
 
     if 'tls' in openvpn:
         if 'ca_cert_file' not in openvpn['tls']:
             raise ConfigError('Must specify "tls ca-cert-file"')
 
         if not (openvpn['mode'] == 'client' and 'auth_file' in openvpn['tls']):
             if 'cert_file' not in openvpn['tls']:
                 raise ConfigError('Missing "tls cert-file"')
 
             if 'key_file' not in openvpn['tls']:
                 raise ConfigError('Missing "tls key-file"')
 
         if {'auth_file', 'crypt_file'} <= set(openvpn['tls']):
             raise ConfigError('TLS auth and crypt are mutually exclusive')
 
         file = dict_search('tls.ca_cert_file', openvpn)
         if file and not checkCertHeader('-----BEGIN CERTIFICATE-----', file):
             raise ConfigError(f'Specified ca-cert-file "{file}" is invalid')
 
         file = dict_search('tls.auth_file', openvpn)
         if file and not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', file):
             raise ConfigError(f'Specified auth-file "{file}" is invalid')
 
         file = dict_search('tls.cert_file', openvpn)
         if file and not checkCertHeader('-----BEGIN CERTIFICATE-----', file):
             raise ConfigError(f'Specified cert-file "{file}" is invalid')
 
         file = dict_search('tls.key_file', openvpn)
         if file and not checkCertHeader('-----BEGIN (?:RSA |EC )?PRIVATE KEY-----', file):
             raise ConfigError(f'Specified key-file "{file}" is not valid')
 
         file = dict_search('tls.crypt_file', openvpn)
         if file and not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', file):
             raise ConfigError(f'Specified TLS crypt-file "{file}" is invalid')
 
         file = dict_search('tls.crl_file', openvpn)
         if file and not checkCertHeader('-----BEGIN X509 CRL-----', file):
             raise ConfigError(f'Specified crl-file "{file} not valid')
 
         file = dict_search('tls.dh_file', openvpn)
         if file and not checkCertHeader('-----BEGIN DH PARAMETERS-----', file):
             raise ConfigError(f'Specified dh-file "{file}" is not valid')
 
         if file and not verify_diffie_hellman_length(file, 2048):
             raise ConfigError(f'Minimum DH key-size is 2048 bits')
 
         tmp = dict_search('tls.role', openvpn)
         if tmp:
             if openvpn['mode'] in ['client', 'server']:
                 if not dict_search('tls.auth_file', openvpn):
                     raise ConfigError('Cannot specify "tls role" in client-server mode')
 
             if tmp == 'active':
                 if openvpn['protocol'] == 'tcp-passive':
                     raise ConfigError('Cannot specify "tcp-passive" when "tls role" is "active"')
 
                 if dict_search('tls.dh_file', openvpn):
                     raise ConfigError('Cannot specify "tls dh-file" when "tls role" is "active"')
 
             elif tmp == 'passive':
                 if openvpn['protocol'] == 'tcp-active':
                     raise ConfigError('Cannot specify "tcp-active" when "tls role" is "passive"')
 
                 if not dict_search('tls.dh_file', openvpn):
                     raise ConfigError('Must specify "tls dh-file" when "tls role" is "passive"')
 
         file = dict_search('tls.key_file', openvpn)
         if file and checkCertHeader('-----BEGIN EC PRIVATE KEY-----', file):
             if dict_search('tls.dh_file', openvpn):
                 print('Warning: using dh-file and EC keys simultaneously will ' \
                       'lead to DH ciphers being used instead of ECDH')
 
     if dict_search('encryption.cipher', openvpn) == 'none':
         print('Warning: "encryption none" was specified!')
         print('No encryption will be performed and data is transmitted in ' \
               'plain text over the network!')
 
     #
     # Auth user/pass
     #
     if (dict_search('authentication.username', openvpn) and not
         dict_search('authentication.password', openvpn)):
             raise ConfigError('Password for authentication is missing')
 
     if (dict_search('authentication.password', openvpn) and not
         dict_search('authentication.username', openvpn)):
             raise ConfigError('Username for authentication is missing')
 
     verify_vrf(openvpn)
 
     return None
 
 def generate(openvpn):
     interface = openvpn['ifname']
     directory = os.path.dirname(cfg_file.format(**openvpn))
 
     # we can't know in advance which clients have been removed,
     # thus all client configs will be removed and re-added on demand
     ccd_dir = os.path.join(directory, 'ccd', interface)
     if os.path.isdir(ccd_dir):
         rmtree(ccd_dir, ignore_errors=True)
 
     if 'deleted' in openvpn or 'disable' in openvpn:
         return None
 
     # create client config directory on demand
     if not os.path.exists(ccd_dir):
         os.makedirs(ccd_dir, 0o755)
         chown(ccd_dir, user, group)
 
     # Fix file permissons for keys
     fix_permissions = []
 
     tmp = dict_search('shared_secret_key_file', openvpn)
     if tmp: fix_permissions.append(openvpn['shared_secret_key_file'])
 
     tmp = dict_search('tls.key_file', openvpn)
     if tmp: fix_permissions.append(tmp)
 
     # Generate User/Password authentication file
     if 'authentication' in openvpn:
         render(openvpn['auth_user_pass_file'], 'openvpn/auth.pw.tmpl', openvpn,
-               trim_blocks=True, user=user, group=group, permission=0o600)
+               user=user, group=group, permission=0o600)
     else:
         # delete old auth file if present
         if os.path.isfile(openvpn['auth_user_pass_file']):
             os.remove(openvpn['auth_user_pass_file'])
 
     # Generate client specific configuration
     if dict_search('server.client', openvpn):
         for client, client_config in dict_search('server.client', openvpn).items():
             client_file = os.path.join(ccd_dir, client)
 
             # Our client need's to know its subnet mask ...
             client_config['server_subnet'] = dict_search('server.subnet', openvpn)
 
             render(client_file, 'openvpn/client.conf.tmpl', client_config,
-                   trim_blocks=True, user=user, group=group)
+                   user=user, group=group)
 
     # we need to support quoting of raw parameters from OpenVPN CLI
     # see https://phabricator.vyos.net/T1632
     render(cfg_file.format(**openvpn), 'openvpn/server.conf.tmpl', openvpn,
-           trim_blocks=True, formater=lambda _: _.replace("&quot;", '"'),
-           user=user, group=group)
+           formater=lambda _: _.replace("&quot;", '"'), user=user, group=group)
 
     # Fixup file permissions
     for file in fix_permissions:
         chmod_600(file)
 
     return None
 
 def apply(openvpn):
     interface = openvpn['ifname']
     call(f'systemctl stop openvpn@{interface}.service')
 
     # Do some cleanup when OpenVPN is disabled/deleted
     if 'deleted' in openvpn or 'disable' in openvpn:
         # cleanup old configuration files
         cleanup = []
         cleanup.append(cfg_file.format(**openvpn))
         cleanup.append(openvpn['auth_user_pass_file'])
 
         for file in cleanup:
             if os.path.isfile(file):
                 os.unlink(file)
 
         if interface in interfaces():
             VTunIf(interface).remove()
 
         return None
 
     # No matching OpenVPN process running - maybe it got killed or none
     # existed - nevertheless, spawn new OpenVPN process
     call(f'systemctl start openvpn@{interface}.service')
 
     conf = VTunIf.get_config()
     conf['device_type'] = openvpn['device_type']
 
     o = VTunIf(interface, **conf)
     o.update(openvpn)
 
     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/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py
index ee3b142c8..c31e49574 100755
--- a/src/conf_mode/interfaces-pppoe.py
+++ b/src/conf_mode/interfaces-pppoe.py
@@ -1,137 +1,137 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-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 copy import deepcopy
 from netifaces import interfaces
 
 from vyos.config import Config
 from vyos.configdict import get_interface_dict
 from vyos.configverify import verify_source_interface
 from vyos.configverify import verify_vrf
 from vyos.configverify import verify_mtu_ipv6
 from vyos.template import render
 from vyos.util import call
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     """
     Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
     interface name will be added or a deleted flag
     """
     if config:
         conf = config
     else:
         conf = Config()
     base = ['interfaces', 'pppoe']
     pppoe = get_interface_dict(conf, base)
 
     # PPPoE is "special" the default MTU is 1492 - update accordingly
     # as the config_level is already st in get_interface_dict() - we can use []
     tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True)
     if 'mtu' not in tmp:
         pppoe['mtu'] = '1492'
 
     return pppoe
 
 def verify(pppoe):
     if 'deleted' in pppoe:
         # bail out early
         return None
 
     verify_source_interface(pppoe)
     verify_vrf(pppoe)
     verify_mtu_ipv6(pppoe)
 
     if {'connect_on_demand', 'vrf'} <= set(pppoe):
         raise ConfigError('On-demand dialing and VRF can not be used at the same time')
 
     return None
 
 def generate(pppoe):
     # set up configuration file path variables where our templates will be
     # rendered into
     ifname = pppoe['ifname']
     config_pppoe = f'/etc/ppp/peers/{ifname}'
     script_pppoe_pre_up = f'/etc/ppp/ip-pre-up.d/1000-vyos-pppoe-{ifname}'
     script_pppoe_ip_up = f'/etc/ppp/ip-up.d/1000-vyos-pppoe-{ifname}'
     script_pppoe_ip_down = f'/etc/ppp/ip-down.d/1000-vyos-pppoe-{ifname}'
     script_pppoe_ipv6_up = f'/etc/ppp/ipv6-up.d/1000-vyos-pppoe-{ifname}'
     config_wide_dhcp6c = f'/run/dhcp6c/dhcp6c.{ifname}.conf'
 
     config_files = [config_pppoe, script_pppoe_pre_up, script_pppoe_ip_up,
                     script_pppoe_ip_down, script_pppoe_ipv6_up, config_wide_dhcp6c]
 
     if 'deleted' in pppoe:
         # stop DHCPv6-PD client
         call(f'systemctl stop dhcp6c@{ifname}.service')
         # Hang-up PPPoE connection
         call(f'systemctl stop ppp@{ifname}.service')
 
         # Delete PPP configuration files
         for file in config_files:
             if os.path.exists(file):
                 os.unlink(file)
 
         return None
 
     # Create PPP configuration files
-    render(config_pppoe, 'pppoe/peer.tmpl',
-           pppoe, trim_blocks=True, permission=0o755)
+    render(config_pppoe, 'pppoe/peer.tmpl', pppoe, permission=0o755)
+
     # Create script for ip-pre-up.d
-    render(script_pppoe_pre_up, 'pppoe/ip-pre-up.script.tmpl',
-           pppoe, trim_blocks=True, permission=0o755)
+    render(script_pppoe_pre_up, 'pppoe/ip-pre-up.script.tmpl', pppoe,
+           permission=0o755)
     # Create script for ip-up.d
-    render(script_pppoe_ip_up, 'pppoe/ip-up.script.tmpl',
-           pppoe, trim_blocks=True, permission=0o755)
+    render(script_pppoe_ip_up, 'pppoe/ip-up.script.tmpl', pppoe,
+           permission=0o755)
     # Create script for ip-down.d
-    render(script_pppoe_ip_down, 'pppoe/ip-down.script.tmpl',
-           pppoe, trim_blocks=True, permission=0o755)
+    render(script_pppoe_ip_down, 'pppoe/ip-down.script.tmpl', pppoe,
+           permission=0o755)
     # Create script for ipv6-up.d
-    render(script_pppoe_ipv6_up, 'pppoe/ipv6-up.script.tmpl',
-           pppoe, trim_blocks=True, permission=0o755)
+    render(script_pppoe_ipv6_up, 'pppoe/ipv6-up.script.tmpl', pppoe,
+           permission=0o755)
 
     if 'dhcpv6_options' in pppoe and 'pd' in pppoe['dhcpv6_options']:
         # ipv6.tmpl relies on ifname - this should be made consitent in the
         # future better then double key-ing the same value
-        render(config_wide_dhcp6c, 'dhcp-client/ipv6.tmpl', pppoe, trim_blocks=True)
+        render(config_wide_dhcp6c, 'dhcp-client/ipv6.tmpl', pppoe)
 
     return None
 
 def apply(pppoe):
     if 'deleted' in pppoe:
         # bail out early
         return None
 
     if 'disable' not in pppoe:
         # Dial PPPoE connection
         call('systemctl restart ppp@{ifname}.service'.format(**pppoe))
 
     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/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index d302c7df7..b25fcd4e0 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -1,290 +1,292 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-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 re import findall
 from netaddr import EUI, mac_unix_expanded
 
 from vyos.config import Config
 from vyos.configdict import get_interface_dict
 from vyos.configdict import dict_merge
 from vyos.configverify import verify_address
 from vyos.configverify import verify_bridge_delete
 from vyos.configverify import verify_dhcpv6
 from vyos.configverify import verify_source_interface
 from vyos.configverify import verify_vlan_config
 from vyos.configverify import verify_vrf
 from vyos.ifconfig import WiFiIf
 from vyos.template import render
 from vyos.util import call
 from vyos.util import dict_search
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 # XXX: wpa_supplicant works on the source interface
 wpa_suppl_conf = '/run/wpa_supplicant/{ifname}.conf'
 hostapd_conf = '/run/hostapd/{ifname}.conf'
 
 def find_other_stations(conf, base, ifname):
     """
     Only one wireless interface per phy can be in station mode -
     find all interfaces attached to a phy which run in station mode
     """
     old_level = conf.get_level()
     conf.set_level(base)
     dict = {}
     for phy in os.listdir('/sys/class/ieee80211'):
         list = []
         for interface in conf.list_nodes([]):
             if interface == ifname:
                 continue
             # the following node is mandatory
             if conf.exists([interface, 'physical-device', phy]):
                 tmp = conf.return_value([interface, 'type'])
                 if tmp == 'station':
                     list.append(interface)
         if list:
             dict.update({phy: list})
     conf.set_level(old_level)
     return dict
 
 def get_config(config=None):
     """
     Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
     interface name will be added or a deleted flag
     """
     if config:
         conf = config
     else:
         conf = Config()
     base = ['interfaces', 'wireless']
 
     wifi = get_interface_dict(conf, base)
 
     # Cleanup "delete" default values when required user selectable values are
     # not defined at all
     tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True)
     if not (dict_search('security.wpa.passphrase', tmp) or
             dict_search('security.wpa.radius', tmp)):
         del wifi['security']['wpa']
 
     # 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('security.wpa.radius.server.port', wifi):
         del wifi['security']['wpa']['radius']['server']['port']
         if not len(wifi['security']['wpa']['radius']['server']):
             del wifi['security']['wpa']['radius']
         if not len(wifi['security']['wpa']):
             del wifi['security']['wpa']
         if not len(wifi['security']):
             del wifi['security']
 
     if 'security' in wifi and 'wpa' in wifi['security']:
         wpa_cipher = wifi['security']['wpa'].get('cipher')
         wpa_mode = wifi['security']['wpa'].get('mode')
         if not wpa_cipher:
             tmp = None
             if wpa_mode == 'wpa':
                 tmp = {'security': {'wpa': {'cipher' : ['TKIP', 'CCMP']}}}
             elif wpa_mode == 'wpa2':
                 tmp = {'security': {'wpa': {'cipher' : ['CCMP']}}}
             elif wpa_mode == 'both':
                 tmp = {'security': {'wpa': {'cipher' : ['CCMP', 'TKIP']}}}
 
             if tmp: wifi = dict_merge(tmp, wifi)
 
     # Only one wireless interface per phy can be in station mode
     tmp = find_other_stations(conf, base, wifi['ifname'])
     if tmp: wifi['station_interfaces'] = tmp
 
     # Add individual RADIUS server default values
     if dict_search('security.wpa.radius.server', wifi):
         default_values = defaults(base + ['security', 'wpa', 'radius', 'server'])
 
         for server in dict_search('security.wpa.radius.server', wifi):
             wifi['security']['wpa']['radius']['server'][server] = dict_merge(
                 default_values, wifi['security']['wpa']['radius']['server'][server])
 
     return wifi
 
 def verify(wifi):
     if 'deleted' in wifi:
         verify_bridge_delete(wifi)
         return None
 
     if 'physical_device' not in wifi:
         raise ConfigError('You must specify a physical-device "phy"')
 
     if 'type' not in wifi:
         raise ConfigError('You must specify a WiFi mode')
 
     if 'ssid' not in wifi and wifi['type'] != 'monitor':
         raise ConfigError('SSID must be configured')
 
     if wifi['type'] == 'access-point':
         if 'country_code' not in wifi:
             raise ConfigError('Wireless country-code is mandatory')
 
         if 'channel' not in wifi:
             raise ConfigError('Wireless channel must be configured!')
 
     if 'security' in wifi:
         if {'wep', 'wpa'} <= set(wifi.get('security', {})):
             raise ConfigError('Must either use WEP or WPA security!')
 
         if 'wep' in wifi['security']:
             if 'key' in wifi['security']['wep'] and len(wifi['security']['wep']) > 4:
                 raise ConfigError('No more then 4 WEP keys configurable')
             elif 'key' not in wifi['security']['wep']:
                 raise ConfigError('Security WEP configured - missing WEP keys!')
 
         elif 'wpa' in wifi['security']:
             wpa = wifi['security']['wpa']
             if not any(i in ['passphrase', 'radius'] for i in wpa):
                 raise ConfigError('Misssing WPA key or RADIUS server')
 
             if 'radius' in wpa:
                 if 'server' in wpa['radius']:
                     for server in wpa['radius']['server']:
                         if 'key' not in wpa['radius']['server'][server]:
                             raise ConfigError(f'Misssing RADIUS shared secret key for server: {server}')
 
     if 'capabilities' in wifi:
         capabilities = wifi['capabilities']
         if 'vht' in capabilities:
             if 'ht' not in capabilities:
                 raise ConfigError('Specify HT flags if you want to use VHT!')
 
             if {'beamform', 'antenna_count'} <= set(capabilities.get('vht', {})):
                 if capabilities['vht']['antenna_count'] == '1':
                     raise ConfigError('Cannot use beam forming with just one antenna!')
 
                 if capabilities['vht']['beamform'] == 'single-user-beamformer':
                     if int(capabilities['vht']['antenna_count']) < 3:
                         # Nasty Gotcha: see https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf lines 692-705
                         raise ConfigError('Single-user beam former requires at least 3 antennas!')
 
     if 'station_interfaces' in wifi and wifi['type'] == 'station':
         phy = wifi['physical_device']
         if phy in wifi['station_interfaces']:
             if len(wifi['station_interfaces'][phy]) > 0:
                 raise ConfigError('Only one station per wireless physical interface possible!')
 
     verify_address(wifi)
     verify_vrf(wifi)
 
     # use common function to verify VLAN configuration
     verify_vlan_config(wifi)
 
     return None
 
 def generate(wifi):
     interface = wifi['ifname']
 
     # always stop hostapd service first before reconfiguring it
     call(f'systemctl stop hostapd@{interface}.service')
     # always stop wpa_supplicant service first before reconfiguring it
     call(f'systemctl stop wpa_supplicant@{interface}.service')
 
     # Delete config files if interface is removed
     if 'deleted' in wifi:
         if os.path.isfile(hostapd_conf.format(**wifi)):
             os.unlink(hostapd_conf.format(**wifi))
 
         if os.path.isfile(wpa_suppl_conf.format(**wifi)):
             os.unlink(wpa_suppl_conf.format(**wifi))
 
         return None
 
     if 'mac' not in wifi:
         # http://wiki.stocksy.co.uk/wiki/Multiple_SSIDs_with_hostapd
         # generate locally administered MAC address from used phy interface
         with open('/sys/class/ieee80211/{physical_device}/addresses'.format(**wifi), 'r') as f:
             # some PHYs tend to have multiple interfaces and thus supply multiple MAC
             # addresses - we only need the first one for our calculation
             tmp = f.readline().rstrip()
             tmp = EUI(tmp).value
             # mask last nibble from the MAC address
             tmp &= 0xfffffffffff0
             # set locally administered bit in MAC address
             tmp |= 0x020000000000
             # we now need to add an offset to our MAC address indicating this
             # subinterfaces index
             tmp += int(findall(r'\d+', interface)[0])
 
             # 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
             wifi['mac'] = str(mac)
 
     # XXX: Jinja2 can not operate on a dictionary key when it starts of with a number
     if '40mhz_incapable' in (dict_search('capabilities.ht', wifi) or []):
         wifi['capabilities']['ht']['fourtymhz_incapable'] = wifi['capabilities']['ht']['40mhz_incapable']
         del wifi['capabilities']['ht']['40mhz_incapable']
 
     # render appropriate new config files depending on access-point or station mode
     if wifi['type'] == 'access-point':
-        render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.tmpl', wifi, trim_blocks=True)
+        render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.tmpl',
+               wifi)
 
     elif wifi['type'] == 'station':
-        render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.tmpl', wifi, trim_blocks=True)
+        render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.tmpl',
+               wifi)
 
     return None
 
 def apply(wifi):
     interface = wifi['ifname']
     if 'deleted' in wifi:
         WiFiIf(interface).remove()
     else:
         # This is a special type of interface which needs additional parameters
         # when created using iproute2. Instead of passing a ton of arguments,
         # use a dictionary provided by the interface class which holds all the
         # options necessary.
         conf = WiFiIf.get_config()
 
         # Assign WiFi instance configuration parameters to config dict
         conf['phy'] = wifi['physical_device']
 
         # Finally create the new interface
         w = WiFiIf(interface, **conf)
         w.update(wifi)
 
         # Enable/Disable interface - interface is always placed in
         # administrative down state in WiFiIf class
         if 'disable' not in wifi:
             # Physical interface is now configured. Proceed by starting hostapd or
             # wpa_supplicant daemon. When type is monitor we can just skip this.
             if wifi['type'] == 'access-point':
                 call(f'systemctl start hostapd@{interface}.service')
 
             elif wifi['type'] == 'station':
                 call(f'systemctl start wpa_supplicant@{interface}.service')
 
     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/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py
index bce3405d0..976953b31 100755
--- a/src/conf_mode/interfaces-wirelessmodem.py
+++ b/src/conf_mode/interfaces-wirelessmodem.py
@@ -1,132 +1,132 @@
 #!/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.config import Config
 from vyos.configdict import get_interface_dict
 from vyos.configverify import verify_vrf
 from vyos.template import render
 from vyos.util import call
 from vyos.util import check_kmod
 from vyos.util import find_device_file
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 k_mod = ['option', 'usb_wwan', 'usbserial']
 
 def get_config(config=None):
     """
     Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
     interface name will be added or a deleted flag
     """
     if config:
         conf = config
     else:
         conf = Config()
     base = ['interfaces', 'wirelessmodem']
     wwan = get_interface_dict(conf, base)
 
     return wwan
 
 def verify(wwan):
     if 'deleted' in wwan:
         return None
 
     if not 'apn' in wwan:
         raise ConfigError('No APN configured for "{ifname}"'.format(**wwan))
 
     if not 'device' in wwan:
         raise ConfigError('Physical "device" must be configured')
 
     # we can not use isfile() here as Linux device files are no regular files
     # thus the check will return False
     dev_path = find_device_file(wwan['device'])
     if dev_path is None or not os.path.exists(dev_path):
         raise ConfigError('Device "{device}" does not exist'.format(**wwan))
 
     verify_vrf(wwan)
 
     return None
 
 def generate(wwan):
     # set up configuration file path variables where our templates will be
     # rendered into
     ifname = wwan['ifname']
     config_wwan = f'/etc/ppp/peers/{ifname}'
     config_wwan_chat = f'/etc/ppp/peers/chat.{ifname}'
     script_wwan_pre_up = f'/etc/ppp/ip-pre-up.d/1010-vyos-wwan-{ifname}'
     script_wwan_ip_up = f'/etc/ppp/ip-up.d/1010-vyos-wwan-{ifname}'
     script_wwan_ip_down = f'/etc/ppp/ip-down.d/1010-vyos-wwan-{ifname}'
 
     config_files = [config_wwan, config_wwan_chat, script_wwan_pre_up,
                     script_wwan_ip_up, script_wwan_ip_down]
 
     # Always hang-up WWAN connection prior generating new configuration file
     call(f'systemctl stop ppp@{ifname}.service')
 
     if 'deleted' in wwan:
         # Delete PPP configuration files
         for file in config_files:
             if os.path.exists(file):
                 os.unlink(file)
 
     else:
         wwan['device'] = find_device_file(wwan['device'])
 
         # Create PPP configuration files
-        render(config_wwan, 'wwan/peer.tmpl', wwan, trim_blocks=True)
+        render(config_wwan, 'wwan/peer.tmpl', wwan)
         # Create PPP chat script
-        render(config_wwan_chat, 'wwan/chat.tmpl', wwan, trim_blocks=True)
+        render(config_wwan_chat, 'wwan/chat.tmpl', wwan)
 
         # generated script file must be executable
 
         # Create script for ip-pre-up.d
         render(script_wwan_pre_up, 'wwan/ip-pre-up.script.tmpl',
-               wwan, trim_blocks=True, permission=0o755)
+               wwan, permission=0o755)
         # Create script for ip-up.d
         render(script_wwan_ip_up, 'wwan/ip-up.script.tmpl',
-               wwan, trim_blocks=True, permission=0o755)
+               wwan, permission=0o755)
         # Create script for ip-down.d
         render(script_wwan_ip_down, 'wwan/ip-down.script.tmpl',
-               wwan, trim_blocks=True, permission=0o755)
+               wwan, permission=0o755)
 
     return None
 
 def apply(wwan):
     if 'deleted' in wwan:
         # bail out early
         return None
 
     if not 'disable' in wwan:
         # "dial" WWAN connection
         call('systemctl start ppp@{ifname}.service'.format(**wwan))
 
     return None
 
 if __name__ == '__main__':
     try:
         check_kmod(k_mod)
         c = get_config()
         verify(c)
         generate(c)
         apply(c)
     except ConfigError as e:
         print(e)
         exit(1)
diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py
index 11a5b7aaa..a65e8b567 100755
--- a/src/conf_mode/ipsec-settings.py
+++ b/src/conf_mode/ipsec-settings.py
@@ -1,230 +1,230 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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 re
 import os
 
 from time import sleep
 from sys import exit
 
 from vyos.config import Config
 from vyos import ConfigError
 from vyos.util import call
 from vyos.template import render
 
 from vyos import airbag
 airbag.enable()
 
 ra_conn_name = "remote-access"
 charon_conf_file = "/etc/strongswan.d/charon.conf"
 ipsec_secrets_file = "/etc/ipsec.secrets"
 ipsec_ra_conn_dir = "/etc/ipsec.d/tunnels/"
 ipsec_ra_conn_file = ipsec_ra_conn_dir + ra_conn_name
 ipsec_conf_file = "/etc/ipsec.conf"
 ca_cert_path = "/etc/ipsec.d/cacerts"
 server_cert_path = "/etc/ipsec.d/certs"
 server_key_path = "/etc/ipsec.d/private"
 delim_ipsec_l2tp_begin = "### VyOS L2TP VPN Begin ###"
 delim_ipsec_l2tp_end = "### VyOS L2TP VPN End ###"
 charon_pidfile = "/var/run/charon.pid"
 
 def get_config(config=None):
     if config:
         config = config
     else:
         config = Config()
     data = {"install_routes": "yes"}
 
     if config.exists("vpn ipsec options disable-route-autoinstall"):
         data["install_routes"] = "no"
 
     if config.exists("vpn ipsec ipsec-interfaces interface"):
         data["ipsec_interfaces"] = config.return_values("vpn ipsec ipsec-interfaces interface")
 
     # Init config variables
     data["delim_ipsec_l2tp_begin"] = delim_ipsec_l2tp_begin
     data["delim_ipsec_l2tp_end"] = delim_ipsec_l2tp_end
     data["ipsec_ra_conn_file"] = ipsec_ra_conn_file
     data["ra_conn_name"] = ra_conn_name
     # Get l2tp ipsec settings
     data["ipsec_l2tp"] = False
     conf_ipsec_command = "vpn l2tp remote-access ipsec-settings " #last space is useful
     if config.exists(conf_ipsec_command):
         data["ipsec_l2tp"] = True
 
         # Authentication params
         if config.exists(conf_ipsec_command + "authentication mode"):
             data["ipsec_l2tp_auth_mode"] = config.return_value(conf_ipsec_command + "authentication mode")
         if config.exists(conf_ipsec_command + "authentication pre-shared-secret"):
             data["ipsec_l2tp_secret"] = config.return_value(conf_ipsec_command + "authentication pre-shared-secret")
 
         # mode x509
         if config.exists(conf_ipsec_command + "authentication x509 ca-cert-file"):
             data["ipsec_l2tp_x509_ca_cert_file"] = config.return_value(conf_ipsec_command + "authentication x509 ca-cert-file")
         if config.exists(conf_ipsec_command + "authentication x509 crl-file"):
             data["ipsec_l2tp_x509_crl_file"] = config.return_value(conf_ipsec_command + "authentication x509 crl-file")
         if config.exists(conf_ipsec_command + "authentication x509 server-cert-file"):
             data["ipsec_l2tp_x509_server_cert_file"] = config.return_value(conf_ipsec_command + "authentication x509 server-cert-file")
             data["server_cert_file_copied"] = server_cert_path+"/"+re.search('\w+(?:\.\w+)*$', config.return_value(conf_ipsec_command + "authentication x509 server-cert-file")).group(0)
         if config.exists(conf_ipsec_command + "authentication x509 server-key-file"):
             data["ipsec_l2tp_x509_server_key_file"] = config.return_value(conf_ipsec_command + "authentication x509 server-key-file")
             data["server_key_file_copied"] = server_key_path+"/"+re.search('\w+(?:\.\w+)*$', config.return_value(conf_ipsec_command + "authentication x509 server-key-file")).group(0)
         if config.exists(conf_ipsec_command + "authentication x509 server-key-password"):
             data["ipsec_l2tp_x509_server_key_password"] = config.return_value(conf_ipsec_command + "authentication x509 server-key-password")
 
         # Common l2tp ipsec params
         if config.exists(conf_ipsec_command + "ike-lifetime"):
             data["ipsec_l2tp_ike_lifetime"] = config.return_value(conf_ipsec_command + "ike-lifetime")
         else:
             data["ipsec_l2tp_ike_lifetime"] = "3600"
 
         if config.exists(conf_ipsec_command + "lifetime"):
             data["ipsec_l2tp_lifetime"] = config.return_value(conf_ipsec_command + "lifetime")
         else:
             data["ipsec_l2tp_lifetime"] = "3600"
 
     if config.exists("vpn l2tp remote-access outside-address"):
         data['outside_addr'] = config.return_value('vpn l2tp remote-access outside-address')
 
     return data
 
 def write_ipsec_secrets(c):
     if c.get("ipsec_l2tp_auth_mode") == "pre-shared-secret":
         secret_txt = "{0}\n{1} %any : PSK \"{2}\"\n{3}\n".format(delim_ipsec_l2tp_begin, c['outside_addr'], c['ipsec_l2tp_secret'], delim_ipsec_l2tp_end)
     elif c.get("ipsec_l2tp_auth_mode") == "x509":
         secret_txt = "{0}\n: RSA {1}\n{2}\n".format(delim_ipsec_l2tp_begin, c['server_key_file_copied'], delim_ipsec_l2tp_end)
 
     old_umask = os.umask(0o077)
     with open(ipsec_secrets_file, 'a+') as f:
         f.write(secret_txt)
     os.umask(old_umask)
 
 def write_ipsec_conf(c):
     ipsec_confg_txt = "{0}\ninclude {1}\n{2}\n".format(delim_ipsec_l2tp_begin, ipsec_ra_conn_file, delim_ipsec_l2tp_end)
 
     old_umask = os.umask(0o077)
     with open(ipsec_conf_file, 'a+') as f:
         f.write(ipsec_confg_txt)
     os.umask(old_umask)
 
 ### Remove config from file by delimiter
 def remove_confs(delim_begin, delim_end, conf_file):
     call("sed -i '/"+delim_begin+"/,/"+delim_end+"/d' "+conf_file)
 
 
 ### Checking certificate storage and notice if certificate not in /config directory
 def check_cert_file_store(cert_name, file_path, dts_path):
     if not re.search('^\/config\/.+', file_path):
         print("Warning: \"" + file_path + "\" lies outside of /config/auth directory. It will not get preserved during image upgrade.")
     #Checking file existence
     if not os.path.isfile(file_path):
       raise ConfigError("L2TP VPN configuration error: Invalid "+cert_name+" \""+file_path+"\"")
     else:
       ### Cpy file to /etc/ipsec.d/certs/ /etc/ipsec.d/cacerts/
       # todo make check
       ret = call('cp -f '+file_path+' '+dts_path)
       if ret:
          raise ConfigError("L2TP VPN configuration error: Cannot copy "+file_path)
 
 def verify(data):
     # l2tp ipsec check
     if data["ipsec_l2tp"]:
         # Checking dependecies for "authentication mode pre-shared-secret"
         if data.get("ipsec_l2tp_auth_mode") == "pre-shared-secret":
             if not data.get("ipsec_l2tp_secret"):
                 raise ConfigError("pre-shared-secret required")
             if not data.get("outside_addr"):
                 raise ConfigError("outside-address not defined")
 
         # Checking dependecies for "authentication mode x509"
         if data.get("ipsec_l2tp_auth_mode") == "x509":
             if not data.get("ipsec_l2tp_x509_server_key_file"):
                 raise ConfigError("L2TP VPN configuration error: \"server-key-file\" not defined.")
             else:
                 check_cert_file_store("server-key-file", data['ipsec_l2tp_x509_server_key_file'], server_key_path)
 
             if not data.get("ipsec_l2tp_x509_server_cert_file"):
                 raise ConfigError("L2TP VPN configuration error: \"server-cert-file\" not defined.")
             else:
                 check_cert_file_store("server-cert-file", data['ipsec_l2tp_x509_server_cert_file'], server_cert_path)
 
             if not data.get("ipsec_l2tp_x509_ca_cert_file"):
                 raise ConfigError("L2TP VPN configuration error: \"ca-cert-file\" must be defined for X.509")
             else:
                 check_cert_file_store("ca-cert-file", data['ipsec_l2tp_x509_ca_cert_file'], ca_cert_path)
 
         if not data.get('ipsec_interfaces'):
            raise ConfigError("L2TP VPN configuration error: \"vpn ipsec ipsec-interfaces\" must be specified.")
 
 def generate(data):
-    render(charon_conf_file, 'ipsec/charon.tmpl', data, trim_blocks=True)
+    render(charon_conf_file, 'ipsec/charon.tmpl', data)
 
     if data["ipsec_l2tp"]:
         remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_secrets_file)
         # old_umask = os.umask(0o077)
-        # render(ipsec_secrets_file, 'ipsec/ipsec.secrets.tmpl', data, trim_blocks=True)
+        # render(ipsec_secrets_file, 'ipsec/ipsec.secrets.tmpl', data)
         # os.umask(old_umask)
         ## Use this method while IPSec CLI handler won't be overwritten to python
         write_ipsec_secrets(data)
 
         old_umask = os.umask(0o077)
 
         # Create tunnels directory if does not exist
         if not os.path.exists(ipsec_ra_conn_dir):
             os.makedirs(ipsec_ra_conn_dir)
 
-        render(ipsec_ra_conn_file, 'ipsec/remote-access.tmpl', data, trim_blocks=True)
+        render(ipsec_ra_conn_file, 'ipsec/remote-access.tmpl', data)
         os.umask(old_umask)
 
         remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_file)
         # old_umask = os.umask(0o077)
-        # render(ipsec_conf_file, 'ipsec/ipsec.conf.tmpl', data, trim_blocks=True)
+        # render(ipsec_conf_file, 'ipsec/ipsec.conf.tmpl', data)
         # os.umask(old_umask)
         ## Use this method while IPSec CLI handler won't be overwritten to python
         write_ipsec_conf(data)
 
     else:
         if os.path.exists(ipsec_ra_conn_file):
             remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_ra_conn_file)
         remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_secrets_file)
         remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_file)
 
 def restart_ipsec():
     call('ipsec restart >&/dev/null')
     # counter for apply swanctl config
     counter = 10
     while counter <= 10:
         if os.path.exists(charon_pidfile):
             call('swanctl -q >&/dev/null')
             break
         counter -=1
         sleep(1)
         if counter == 0:
             raise ConfigError('VPN configuration error: IPSec is not running.')
 
 def apply(data):
     # Restart IPSec daemon
     restart_ipsec()
 
 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/nat.py b/src/conf_mode/nat.py
index b66cd370a..b467f3d74 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -1,290 +1,291 @@
 #!/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 jmespath
 import json
 import os
 
 from copy import deepcopy
 from distutils.version import LooseVersion
 from platform import release as kernel_version
 from sys import exit
 from netifaces import interfaces
 
 from vyos.config import Config
 from vyos.template import render
 from vyos.util import call
 from vyos.util import cmd
 from vyos.util import check_kmod
 from vyos.validate import is_addr_assigned
 from vyos import ConfigError
 
 from vyos import airbag
 airbag.enable()
 
 if LooseVersion(kernel_version()) > LooseVersion('5.1'):
     k_mod = ['nft_nat', 'nft_chain_nat']
 else:
     k_mod = ['nft_nat', 'nft_chain_nat_ipv4']
 
 default_config_data = {
     'deleted': False,
     'destination': [],
     'helper_functions': None,
     'pre_ct_helper': '',
     'pre_ct_conntrack': '',
     'out_ct_helper': '',
     'out_ct_conntrack': '',
     'source': []
 }
 
 iptables_nat_config = '/tmp/vyos-nat-rules.nft'
 
 def get_handler(json, chain, target):
     """ Get nftable rule handler number of given chain/target combination.
     Handler is required when adding NAT/Conntrack helper targets """
     for x in json:
         if x['chain'] != chain:
             continue
         if x['target'] != target:
             continue
         return x['handle']
 
     return None
 
 
 def verify_rule(rule, err_msg):
     """ Common verify steps used for both source and destination NAT """
     if rule['translation_port'] or rule['dest_port'] or rule['source_port']:
         if rule['protocol'] not in ['tcp', 'udp', 'tcp_udp']:
             proto = rule['protocol']
             raise ConfigError(f'{err_msg} ports can only be specified when protocol is "tcp", "udp" or "tcp_udp" (currently "{proto}")')
 
         if '/' in rule['translation_address']:
             raise ConfigError(f'{err_msg}\n' \
                              'Cannot use ports with an IPv4net type translation address as it\n' \
                              'statically maps a whole network of addresses onto another\n' \
                              'network of addresses')
 
 
 def parse_configuration(conf, source_dest):
     """ Common wrapper to read in both NAT source and destination CLI """
     ruleset = []
     base_level = ['nat', source_dest]
     conf.set_level(base_level)
     for number in conf.list_nodes(['rule']):
         rule = {
             'description': '',
             'dest_address': '',
             'dest_port': '',
             'disabled': False,
             'exclude': False,
             'interface_in': '',
             'interface_out': '',
             'log': False,
             'protocol': 'all',
             'number': number,
             'source_address': '',
             'source_prefix': '',
             'source_port': '',
             'translation_address': '',
             'translation_prefix': '',
             'translation_port': ''
         }
         conf.set_level(base_level + ['rule', number])
 
         if conf.exists(['description']):
             rule['description'] = conf.return_value(['description'])
 
         if conf.exists(['destination', 'address']):
             tmp = conf.return_value(['destination', 'address'])
             if tmp.startswith('!'):
                 tmp = tmp.replace('!', '!=')
             rule['dest_address'] = tmp
 
         if conf.exists(['destination', 'port']):
             tmp = conf.return_value(['destination', 'port'])
             if tmp.startswith('!'):
                 tmp = tmp.replace('!', '!=')
             rule['dest_port'] = tmp
 
         if conf.exists(['disable']):
             rule['disabled'] = True
 
         if conf.exists(['exclude']):
             rule['exclude'] = True
 
         if conf.exists(['inbound-interface']):
             rule['interface_in'] = conf.return_value(['inbound-interface'])
 
         if conf.exists(['outbound-interface']):
             rule['interface_out'] = conf.return_value(['outbound-interface'])
 
         if conf.exists(['log']):
             rule['log'] = True
 
         if conf.exists(['protocol']):
             rule['protocol'] = conf.return_value(['protocol'])
 
         if conf.exists(['source', 'address']):
             tmp = conf.return_value(['source', 'address'])
             if tmp.startswith('!'):
                 tmp = tmp.replace('!', '!=')
             rule['source_address'] = tmp
 
         if conf.exists(['source', 'prefix']):
             rule['source_prefix'] = conf.return_value(['source', 'prefix'])
 
         if conf.exists(['source', 'port']):
             tmp = conf.return_value(['source', 'port'])
             if tmp.startswith('!'):
                 tmp = tmp.replace('!', '!=')
             rule['source_port'] = tmp
 
         if conf.exists(['translation', 'address']):
             rule['translation_address'] = conf.return_value(['translation', 'address'])
 
         if conf.exists(['translation', 'prefix']):
             rule['translation_prefix'] = conf.return_value(['translation', 'prefix'])
 
         if conf.exists(['translation', 'port']):
             rule['translation_port'] = conf.return_value(['translation', 'port'])
 
         ruleset.append(rule)
 
     return ruleset
 
 def get_config(config=None):
     nat = deepcopy(default_config_data)
     if config:
         conf = config
     else:
         conf = Config()
 
     # read in current nftable (once) for further processing
     tmp = cmd('nft -j list table raw')
     nftable_json = json.loads(tmp)
 
     # condense the full JSON table into a list with only relevand informations
     pattern = 'nftables[?rule].rule[?expr[].jump].{chain: chain, handle: handle, target: expr[].jump.target | [0]}'
     condensed_json = jmespath.search(pattern, nftable_json)
 
     if not conf.exists(['nat']):
         nat['helper_functions'] = 'remove'
 
         # Retrieve current table handler positions
         nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_HELPER')
         nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK')
         nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_HELPER')
         nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'NAT_CONNTRACK')
 
         nat['deleted'] = True
 
         return nat
 
     # check if NAT connection tracking helpers need to be set up - this has to
     # be done only once
     if not get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK'):
         nat['helper_functions'] = 'add'
 
         # Retrieve current table handler positions
         nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_IGNORE')
         nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_PREROUTING_HOOK')
         nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_IGNORE')
         nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_OUTPUT_HOOK')
 
     # set config level for parsing in NAT configuration
     conf.set_level(['nat'])
 
     # use a common wrapper function to read in the source / destination
     # tree from the config - thus we do not need to replicate almost the
     # same code :-)
     for tgt in ['source', 'destination', 'nptv6']:
         nat[tgt] = parse_configuration(conf, tgt)
 
     return nat
 
 def verify(nat):
     if nat['deleted']:
         # no need to verify the CLI as NAT is going to be deactivated
         return None
 
     if nat['helper_functions']:
         if not (nat['pre_ct_ignore'] or nat['pre_ct_conntrack'] or nat['out_ct_ignore'] or nat['out_ct_conntrack']):
             raise Exception('could not determine nftable ruleset handlers')
 
     for rule in nat['source']:
         interface = rule['interface_out']
         err_msg = f'Source NAT configuration error in rule "{rule["number"]}":'
 
         if interface and interface not in 'any' and interface not in interfaces():
             print(f'Warning: rule "{rule["number"]}" interface "{interface}" does not exist on this system')
 
         if not rule['interface_out']:
             raise ConfigError(f'{err_msg} outbound-interface not specified')
 
         if rule['translation_address']:
             addr = rule['translation_address']
             if addr != 'masquerade':
                 for ip in addr.split('-'):
                     if not is_addr_assigned(ip):
                         print(f'Warning: IP address {ip} does not exist on the system!')
 
         elif not rule['exclude']:
             raise ConfigError(f'{err_msg} translation address not specified')
 
         # common rule verification
         verify_rule(rule, err_msg)
 
     for rule in nat['destination']:
         interface = rule['interface_in']
         err_msg = f'Destination NAT configuration error in rule "{rule["number"]}":'
 
         if interface and interface not in 'any' and interface not in interfaces():
             print(f'Warning: rule "{rule["number"]}" interface "{interface}" does not exist on this system')
 
         if not rule['interface_in']:
             raise ConfigError(f'{err_msg} inbound-interface not specified')
 
         if not rule['translation_address'] and not rule['exclude']:
             raise ConfigError(f'{err_msg} translation address not specified')
 
         # common rule verification
         verify_rule(rule, err_msg)
 
     return None
 
 def generate(nat):
-    render(iptables_nat_config, 'firewall/nftables-nat.tmpl', nat, trim_blocks=True, permission=0o755)
+    render(iptables_nat_config, 'firewall/nftables-nat.tmpl', nat,
+           permission=0o755)
     return None
 
 def apply(nat):
     cmd(f'{iptables_nat_config}')
     if os.path.isfile(iptables_nat_config):
         os.unlink(iptables_nat_config)
 
     return None
 
 if __name__ == '__main__':
     try:
         check_kmod(k_mod)
         c = get_config()
         verify(c)
         generate(c)
         apply(c)
     except ConfigError as e:
         print(e)
         exit(1)
diff --git a/src/conf_mode/ntp.py b/src/conf_mode/ntp.py
index d6453ec83..b102b3e9e 100755
--- a/src/conf_mode/ntp.py
+++ b/src/conf_mode/ntp.py
@@ -1,85 +1,85 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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 vyos.config import Config
 from vyos.configverify import verify_vrf
 from vyos import ConfigError
 from vyos.util import call
 from vyos.template import render
 from vyos import airbag
 airbag.enable()
 
 config_file = r'/etc/ntp.conf'
 systemd_override = r'/etc/systemd/system/ntp.service.d/override.conf'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['system', 'ntp']
 
     ntp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
     return ntp
 
 def verify(ntp):
     # bail out early - looks like removal from running config
     if not ntp:
         return None
 
     if len(ntp.get('allow_clients', {})) and not (len(ntp.get('server', {})) > 0):
         raise ConfigError('NTP server not configured')
 
     verify_vrf(ntp)
     return None
 
 def generate(ntp):
     # bail out early - looks like removal from running config
     if not ntp:
         return None
 
-    render(config_file, 'ntp/ntp.conf.tmpl', ntp, trim_blocks=True)
-    render(systemd_override, 'ntp/override.conf.tmpl', ntp, trim_blocks=True)
+    render(config_file, 'ntp/ntp.conf.tmpl', ntp)
+    render(systemd_override, 'ntp/override.conf.tmpl', ntp)
 
     return None
 
 def apply(ntp):
     if not ntp:
         # NTP support is removed in the commit
         call('systemctl stop ntp.service')
         if os.path.exists(config_file):
             os.unlink(config_file)
         if os.path.isfile(systemd_override):
             os.unlink(systemd_override)
 
     # Reload systemd manager configuration
     call('systemctl daemon-reload')
     if ntp:
         call('systemctl restart ntp.service')
 
     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/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 981ff9fe9..642738b09 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -1,121 +1,119 @@
 #!/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/>.
 
 from sys import exit
 
 from vyos.config import Config
 from vyos.util import call
 from vyos.util import dict_search
 from vyos.template import render
 from vyos.template import render_to_string
 from vyos import ConfigError
 from vyos import frr
 from vyos import airbag
 airbag.enable()
 
 config_file = r'/tmp/bgp.frr'
 
 def get_config():
     conf = Config()
     base = ['protocols', 'nbgp']
     bgp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
 
     # XXX: any reason we can not move this into the FRR template?
     # we shall not call vtysh directly, especially not in get_config()
     if not conf.exists(base):
         bgp = {}
         call('vtysh -c \"conf t\" -c \"no ip protocol bgp\" ')
 
     if not conf.exists(base + ['route-map']):
         call('vtysh -c \"conf t\" -c \"no ip protocol bgp\" ')
 
     return bgp
 
 def verify(bgp):
     if not bgp:
         return None
 
     # Check if declared more than one ASN
     if len(bgp) > 1:
         raise ConfigError('Only one BGP AS can be defined!')
 
     for asn, asn_config in bgp.items():
         # Common verification for both peer-group and neighbor statements
         for neigh in ['neighbor', 'peer_group']:
             # bail out early if there is no neighbor or peer-group statement
             # this also saves one indention level
             if neigh not in asn_config:
                 continue
 
             for neighbor, config in asn_config[neigh].items():
                 if 'remote_as' not in config and 'peer_group' not in config:
                     raise ConfigError(f'BGP remote-as must be specified for "{neighbor}"!')
 
                 if 'remote_as' in config and 'peer_group' in config:
                     raise ConfigError(f'BGP peer-group member "{neighbor}" cannot override remote-as of peer-group!')
 
     return None
 
 def generate(bgp):
     if not bgp:
         bgp['new_frr_config'] = ''
         return None
 
     # only one BGP AS is supported, so we can directly send the first key
     # of the config dict
     asn = list(bgp.keys())[0]
     bgp[asn]['asn'] = asn
 
     # render(config) not needed, its only for debug
-    render(config_file, 'frr/bgp.frr.tmpl', bgp[asn], trim_blocks=True)
-
-    bgp['new_frr_config'] = render_to_string('frr/bgp.frr.tmpl', bgp[asn],
-                                             trim_blocks=True)
+    render(config_file, 'frr/bgp.frr.tmpl', bgp[asn])
+    bgp['new_frr_config'] = render_to_string('frr/bgp.frr.tmpl', bgp[asn])
 
     return None
 
 def apply(bgp):
     # Save original configuration prior to starting any commit actions
     frr_cfg = {}
     frr_cfg['original_config'] = frr.get_configuration(daemon='bgpd')
     frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], bgp['new_frr_config'], from_re='router bgp .*')
 
     # Debugging
     print('')
     print('--------- DEBUGGING ----------')
     print(f'Existing config:\n{frr_cfg["original_config"]}\n\n')
     print(f'Replacement config:\n{bgp["new_frr_config"]}\n\n')
     print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n')
 
     # FRR mark configuration will test for syntax errors and throws an
     # exception if any syntax errors is detected
     frr.mark_configuration(frr_cfg['modified_config'])
 
     # Commit resulting configuration to FRR, this will throw CommitError
     # on failure
     frr.reload_configuration(frr_cfg['modified_config'], daemon='bgpd')
 
     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/protocols_isis.py b/src/conf_mode/protocols_isis.py
index 03e11c6c4..df03fd990 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -1,148 +1,148 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2017-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.config import Config
 from vyos.configdict import node_changed
 from vyos import ConfigError
 from vyos.util import call
 from vyos.template import render
 from vyos.template import render_to_string
 from vyos import frr
 from vyos import airbag
 airbag.enable()
 
 config_file = r'/tmp/isis.frr'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['protocols', 'isis']
 
     isis = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
 
     # determine which members have been removed
     for instance in isis:
         conf.set_level(base + [instance])
         tmp = node_changed(conf, ['interface'])
         if tmp:
             isis[instance].update({'interface_remove': tmp})
 
     return isis
 
 def verify(isis):
     # bail out early - looks like removal from running config
     if not isis:
         return None
 
     for process, isis_config in isis.items():
         # If more then one isis process is defined (Frr only supports one)
         # http://docs.frrouting.org/en/latest/isisd.html#isis-router
         if len(isis) > 1:
             raise ConfigError('Only one isis process can be definded')
 
         # If network entity title (net) not defined
         if 'net' not in isis_config:
             raise ConfigError('ISIS net format iso is mandatory!')
 
         # If interface not set
         if 'interface' not in isis_config:
             raise ConfigError('ISIS interface is mandatory!')
 
         # If md5 and plaintext-password set at the same time
         if 'area_password' in isis_config:
             if {'md5', 'plaintext_password'} <= set(isis_config['encryption']):
                 raise ConfigError('Can not use both md5 and plaintext-password for ISIS area-password!')
 
         # If one param from deley set, but not set others
         if 'spf_delay_ietf' in isis_config:
             required_timers = ['holddown', 'init_delay', 'long_delay', 'short_delay', 'time_to_learn']
             exist_timers = []
             for elm_timer in required_timers:
                 if elm_timer in isis_config['spf_delay_ietf']:
                     exist_timers.append(elm_timer)
 
             exist_timers = set(required_timers).difference(set(exist_timers))
             if len(exist_timers) > 0:
                 raise ConfigError('All types of delay must be specified: ' + ', '.join(exist_timers).replace('_', '-'))
 
         # If Redistribute set, but level don't set
         if 'redistribute' in isis_config:
             proc_level = isis_config.get('level','').replace('-','_')
             for proto, proto_config in isis_config.get('redistribute', {}).get('ipv4', {}).items():
                 if 'level_1' not in proto_config and 'level_2' not in proto_config:
                     raise ConfigError('Redistribute level-1 or level-2 should be specified in \"protocols isis {} redistribute ipv4 {}\"'.format(process, proto))
             for redistribute_level in proto_config.keys():
                 if proc_level and proc_level != 'level_1_2' and proc_level != redistribute_level:
                     raise ConfigError('\"protocols isis {0} redistribute ipv4 {2} {3}\" cannot be used with \"protocols isis {0} level {1}\"'.format(process, proc_level, proto, redistribute_level))
 
     return None
 
 def generate(isis):
     if not isis:
         isis['new_frr_config'] = ''
         return None
 
     # only one ISIS process is supported, so we can directly send the first key
     # of the config dict
     process = list(isis.keys())[0]
     isis[process]['process'] = process
 
     # render(config) not needed, its only for debug
-    render(config_file, 'frr/isis.frr.tmpl', isis[process], trim_blocks=True)
+    render(config_file, 'frr/isis.frr.tmpl', isis[process])
 
     isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl',
-                                              isis[process], trim_blocks=True)
+                                              isis[process])
 
     return None
 
 def apply(isis):
     # Save original configuration prior to starting any commit actions
     frr_cfg = {}
     frr_cfg['original_config'] = frr.get_configuration(daemon='isisd')
     frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], isis['new_frr_config'], from_re='router isis .*')
 
     # Debugging
     print('')
     print('--------- DEBUGGING ----------')
     print(f'Existing config:\n{frr_cfg["original_config"]}\n\n')
     print(f'Replacement config:\n{isis["new_frr_config"]}\n\n')
     print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n')
 
     # FRR mark configuration will test for syntax errors and throws an
     # exception if any syntax errors is detected
     frr.mark_configuration(frr_cfg['modified_config'])
 
     # Commit resulting configuration to FRR, this will throw CommitError
     # on failure
     frr.reload_configuration(frr_cfg['modified_config'], daemon='isisd')
 
     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/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index da298325c..791b18110 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -1,149 +1,148 @@
 #!/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.config import Config
 from vyos.configdict import node_changed
 from vyos.template import render_to_string
 from vyos.util import call
 from vyos.util import dict_search
 from vyos import ConfigError
 from vyos import frr
 from vyos import airbag
 airbag.enable()
 
 config_file = r'/tmp/ldpd.frr'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['protocols', 'mpls']
 
     mpls = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
     return mpls
 
 def verify(mpls):
     # If no config, then just bail out early.
     if not mpls:
         return None
 
     # Checks to see if LDP is properly configured
     if 'ldp' in mpls:
         # If router ID not defined
         if 'router_id' not in mpls['ldp']:
             raise ConfigError('Router ID missing. An LDP router id is mandatory!')
 
         # If interface not set
         if 'interface' not in mpls['ldp']:
             raise ConfigError('LDP interfaces are missing. An LDP interface is mandatory!')
 
         # If transport addresses are not set
         if not dict_search('ldp.discovery.transport_ipv4_address', mpls) and \
            not dict_search('ldp.discovery.transport_ipv6_address', mpls):
                 raise ConfigError('LDP transport address missing!')
 
     return None
 
 def generate(mpls):
     # If there's no MPLS config generated, create dictionary key with no value.
     if not mpls:
         mpls['new_frr_config'] = ''
         return None
 
-    mpls['new_frr_config'] = render_to_string('frr/ldpd.frr.tmpl', mpls,
-                                              trim_blocks=True)
+    mpls['new_frr_config'] = render_to_string('frr/ldpd.frr.tmpl', mpls)
     return None
 
 def apply(mpls):
     # Define dictionary that will load FRR config
     frr_cfg = {}
     # Save original configuration prior to starting any commit actions
     frr_cfg['original_config'] = frr.get_configuration(daemon='ldpd')
     frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], mpls['new_frr_config'], from_re='mpls.*')
 
     # If FRR config is blank, rerun the blank commit three times due to frr-reload
     # behavior/bug not properly clearing out on one commit.
     if mpls['new_frr_config'] == '':
         for x in range(3):
             frr.reload_configuration(frr_cfg['modified_config'], daemon='ldpd')
     elif not 'ldp' in mpls:
         for x in range(3):
             frr.reload_configuration(frr_cfg['modified_config'], daemon='ldpd')
     else:
         # FRR mark configuration will test for syntax errors and throws an
         # exception if any syntax errors is detected
         frr.mark_configuration(frr_cfg['modified_config'])
 
         # Commit resulting configuration to FRR, this will throw CommitError
         # on failure
         frr.reload_configuration(frr_cfg['modified_config'], daemon='ldpd')
 
     # Set number of entries in the platform label tables
     labels = '0'
     if 'interface' in mpls:
         labels = '1048575'
     call(f'sysctl -wq net.mpls.platform_labels={labels}')
 
     # Check for changes in global MPLS options
     if 'parameters' in mpls:
             # Choose whether to copy IP TTL to MPLS header TTL
         if 'no_propagate_ttl' in mpls['parameters']:
             call('sysctl -wq net.mpls.ip_ttl_propagate=0')
             # Choose whether to limit maximum MPLS header TTL
         if 'maximum_ttl' in mpls['parameters']:
             ttl = mpls['parameters']['maximum_ttl']
             call(f'sysctl -wq net.mpls.default_ttl={ttl}')
     else:
         # Set default global MPLS options if not defined.
         call('sysctl -wq net.mpls.ip_ttl_propagate=1')
         call('sysctl -wq net.mpls.default_ttl=255')
 
     # Enable and disable MPLS processing on interfaces per configuration
     if 'interface' in mpls:
         system_interfaces = []
         system_interfaces.append(((os.popen('sysctl net.mpls.conf').read()).split('\n')))
         del system_interfaces[0][-1]
         for configured_interface in mpls['interface']:
             for system_interface in system_interfaces[0]:
                 if configured_interface in system_interface:
                     call(f'sysctl -wq net.mpls.conf.{configured_interface}.input=1')
                 elif system_interface.endswith(' = 1'):
                     system_interface = system_interface.replace(' = 1', '=0')
                     call(f'sysctl -wq {system_interface}')
     else:
         # If MPLS interfaces are not configured, set MPLS processing disabled
         system_interfaces = []
         system_interfaces.append(((os.popen('sysctl net.mpls.conf').read()).replace(" = 1", "=0")).split('\n'))
         del system_interfaces[0][-1]
         for interface in (system_interfaces[0]):
             call(f'sysctl -wq {interface}')
 
     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_ids_fastnetmon.py b/src/conf_mode/service_ids_fastnetmon.py
index 27d0ee60c..67edeb630 100755
--- a/src/conf_mode/service_ids_fastnetmon.py
+++ b/src/conf_mode/service_ids_fastnetmon.py
@@ -1,92 +1,92 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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.config import Config
 from vyos import ConfigError
 from vyos.util import call
 from vyos.template import render
 from vyos import airbag
 airbag.enable()
 
 config_file = r'/etc/fastnetmon.conf'
 networks_list = r'/etc/networks_list'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['service', 'ids', 'ddos-protection']
     fastnetmon = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
     return fastnetmon
 
 def verify(fastnetmon):
     if not fastnetmon:
         return None
 
     if not "mode" in fastnetmon:
         raise ConfigError('ddos-protection mode is mandatory!')
 
     if not "network" in fastnetmon:
         raise ConfigError('Required define network!')
 
     if not "listen_interface" in fastnetmon:
         raise ConfigError('Define listen-interface is mandatory!')
 
     if "alert_script" in fastnetmon:
         if os.path.isfile(fastnetmon["alert_script"]):
             # Check script permissions
             if not os.access(fastnetmon["alert_script"], os.X_OK):
                 raise ConfigError('Script {0} does not have permissions for execution'.format(fastnetmon["alert_script"]))
         else:
-           raise ConfigError('File {0} does not exists!'.format(fastnetmon["alert_script"])) 
+           raise ConfigError('File {0} does not exists!'.format(fastnetmon["alert_script"]))
 
 def generate(fastnetmon):
     if not fastnetmon:
         if os.path.isfile(config_file):
             os.unlink(config_file)
         if os.path.isfile(networks_list):
             os.unlink(networks_list)
 
         return
 
-    render(config_file, 'ids/fastnetmon.tmpl', fastnetmon, trim_blocks=True)
-    render(networks_list, 'ids/fastnetmon_networks_list.tmpl', fastnetmon, trim_blocks=True)
+    render(config_file, 'ids/fastnetmon.tmpl', fastnetmon)
+    render(networks_list, 'ids/fastnetmon_networks_list.tmpl', fastnetmon)
 
     return None
 
 def apply(fastnetmon):
     if not fastnetmon:
         # Stop fastnetmon service if removed
         call('systemctl stop fastnetmon.service')
     else:
         call('systemctl restart fastnetmon.service')
 
     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_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index 68c554360..f676fdbbe 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -1,318 +1,318 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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 re
 
 from copy import deepcopy
 from stat import S_IRUSR, S_IWUSR, S_IRGRP
 from sys import exit
 
 from vyos.config import Config
 from vyos.template import render
 from vyos.template import is_ipv4
 from vyos.template import is_ipv6
 from vyos.util import call, get_half_cpus
 from vyos import ConfigError
 
 from vyos import airbag
 airbag.enable()
 
 ipoe_conf = '/run/accel-pppd/ipoe.conf'
 ipoe_chap_secrets = '/run/accel-pppd/ipoe.chap-secrets'
 
 default_config_data = {
     'auth_mode': 'local',
     'auth_interfaces': [],
     'chap_secrets_file': ipoe_chap_secrets, # used in Jinja2 template
     'interfaces': [],
     'dnsv4': [],
     'dnsv6': [],
     'client_ipv6_pool': [],
     'client_ipv6_delegate_prefix': [],
     'radius_server': [],
     'radius_acct_inter_jitter': '',
     'radius_acct_tmo': '3',
     'radius_max_try': '3',
     'radius_timeout': '3',
     'radius_nas_id': '',
     'radius_nas_ip': '',
     'radius_source_address': '',
     'radius_shaper_attr': '',
     'radius_shaper_vendor': '',
     'radius_dynamic_author': '',
     'thread_cnt': get_half_cpus()
 }
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base_path = ['service', 'ipoe-server']
     if not conf.exists(base_path):
         return None
 
     conf.set_level(base_path)
     ipoe = deepcopy(default_config_data)
 
     for interface in conf.list_nodes(['interface']):
         tmp  = {
             'mode': 'L2',
             'name': interface,
             'shared': '1',
             # may need a config option, can be dhcpv4 or up for unclassified pkts
             'sess_start': 'dhcpv4',
             'range': None,
             'ifcfg': '1',
             'vlan_mon': []
         }
 
         conf.set_level(base_path + ['interface', interface])
 
         if conf.exists(['network-mode']):
             tmp['mode'] = conf.return_value(['network-mode'])
 
         if conf.exists(['network']):
             mode = conf.return_value(['network'])
             if mode == 'vlan':
                 tmp['shared'] = '0'
 
                 if conf.exists(['vlan-id']):
                     tmp['vlan_mon'] += conf.return_values(['vlan-id'])
 
                 if conf.exists(['vlan-range']):
                     tmp['vlan_mon'] += conf.return_values(['vlan-range'])
 
         if conf.exists(['client-subnet']):
             tmp['range'] = conf.return_value(['client-subnet'])
 
         ipoe['interfaces'].append(tmp)
 
     conf.set_level(base_path)
 
     if conf.exists(['name-server']):
         for name_server in conf.return_values(['name-server']):
             if is_ipv4(name_server):
                 ipoe['dnsv4'].append(name_server)
             else:
                 ipoe['dnsv6'].append(name_server)
 
     if conf.exists(['authentication', 'mode']):
         ipoe['auth_mode'] = conf.return_value(['authentication', 'mode'])
 
     if conf.exists(['authentication', 'interface']):
         for interface in conf.list_nodes(['authentication', 'interface']):
             tmp = {
                 'name': interface,
                 'mac': []
             }
             for mac in conf.list_nodes(['authentication', 'interface', interface, 'mac-address']):
                 client = {
                     'address': mac,
                     'rate_download': '',
                     'rate_upload': '',
                     'vlan_id': ''
                 }
                 conf.set_level(base_path + ['authentication', 'interface', interface, 'mac-address', mac])
 
                 if conf.exists(['rate-limit', 'download']):
                     client['rate_download'] = conf.return_value(['rate-limit', 'download'])
 
                 if conf.exists(['rate-limit', 'upload']):
                     client['rate_upload'] = conf.return_value(['rate-limit', 'upload'])
 
                 if conf.exists(['vlan-id']):
                     client['vlan'] = conf.return_value(['vlan-id'])
 
                 tmp['mac'].append(client)
 
             ipoe['auth_interfaces'].append(tmp)
 
     conf.set_level(base_path)
 
     #
     # authentication mode radius servers and settings
     if conf.exists(['authentication', 'mode', 'radius']):
         for server in conf.list_nodes(['authentication', 'radius', 'server']):
             radius = {
                 'server' : server,
                 'key' : '',
                 'fail_time' : 0,
                 'port' : '1812',
                 'acct_port' : '1813'
             }
 
             conf.set_level(base_path + ['authentication', 'radius', 'server', server])
 
             if conf.exists(['fail-time']):
                 radius['fail_time'] = conf.return_value(['fail-time'])
 
             if conf.exists(['port']):
                 radius['port'] = conf.return_value(['port'])
 
             if conf.exists(['acct-port']):
                 radius['acct_port'] = conf.return_value(['acct-port'])
 
             if conf.exists(['key']):
                 radius['key'] = conf.return_value(['key'])
 
             if not conf.exists(['disable']):
                 ipoe['radius_server'].append(radius)
 
     #
     # advanced radius-setting
     conf.set_level(base_path + ['authentication', 'radius'])
 
     if conf.exists(['acct-interim-jitter']):
         ipoe['radius_acct_inter_jitter'] = conf.return_value(['acct-interim-jitter'])
 
     if conf.exists(['acct-timeout']):
         ipoe['radius_acct_tmo'] = conf.return_value(['acct-timeout'])
 
     if conf.exists(['max-try']):
         ipoe['radius_max_try'] = conf.return_value(['max-try'])
 
     if conf.exists(['timeout']):
         ipoe['radius_timeout'] = conf.return_value(['timeout'])
 
     if conf.exists(['nas-identifier']):
         ipoe['radius_nas_id'] = conf.return_value(['nas-identifier'])
 
     if conf.exists(['nas-ip-address']):
         ipoe['radius_nas_ip'] = conf.return_value(['nas-ip-address'])
 
     if conf.exists(['source-address']):
         ipoe['radius_source_address'] = conf.return_value(['source-address'])
 
     # Dynamic Authorization Extensions (DOA)/Change Of Authentication (COA)
     if conf.exists(['dynamic-author']):
         dae = {
             'port' : '',
             'server' : '',
             'key' : ''
         }
 
         if conf.exists(['dynamic-author', 'server']):
             dae['server'] = conf.return_value(['dynamic-author', 'server'])
 
         if conf.exists(['dynamic-author', 'port']):
             dae['port'] = conf.return_value(['dynamic-author', 'port'])
 
         if conf.exists(['dynamic-author', 'key']):
             dae['key'] = conf.return_value(['dynamic-author', 'key'])
 
         ipoe['radius_dynamic_author'] = dae
 
 
     conf.set_level(base_path)
     if conf.exists(['client-ipv6-pool', 'prefix']):
         for prefix in conf.list_nodes(['client-ipv6-pool', 'prefix']):
             tmp = {
                 'prefix': prefix,
                 'mask': '64'
             }
 
             if conf.exists(['client-ipv6-pool', 'prefix', prefix, 'mask']):
                 tmp['mask'] = conf.return_value(['client-ipv6-pool', 'prefix', prefix, 'mask'])
 
             ipoe['client_ipv6_pool'].append(tmp)
 
 
     if conf.exists(['client-ipv6-pool', 'delegate']):
         for prefix in conf.list_nodes(['client-ipv6-pool', 'delegate']):
             tmp = {
                 'prefix': prefix,
                 'mask': ''
             }
 
             if conf.exists(['client-ipv6-pool', 'delegate', prefix, 'delegation-prefix']):
                 tmp['mask'] = conf.return_value(['client-ipv6-pool', 'delegate', prefix, 'delegation-prefix'])
 
             ipoe['client_ipv6_delegate_prefix'].append(tmp)
 
     return ipoe
 
 
 def verify(ipoe):
     if not ipoe:
         return None
 
     if not ipoe['interfaces']:
         raise ConfigError('No IPoE interface configured')
 
     for interface in ipoe['interfaces']:
         if not interface['range']:
             raise ConfigError(f'No IPoE client subnet defined on interface "{ interface }"')
 
     if len(ipoe['dnsv4']) > 2:
         raise ConfigError('Not more then two IPv4 DNS name-servers can be configured')
 
     if len(ipoe['dnsv6']) > 3:
         raise ConfigError('Not more then three IPv6 DNS name-servers can be configured')
 
     if ipoe['auth_mode'] == 'radius':
         if len(ipoe['radius_server']) == 0:
             raise ConfigError('RADIUS authentication requires at least one server')
 
         for radius in ipoe['radius_server']:
             if not radius['key']:
                 server = radius['server']
                 raise ConfigError(f'Missing RADIUS secret key for server "{ server }"')
 
     if ipoe['client_ipv6_delegate_prefix'] and not ipoe['client_ipv6_pool']:
         raise ConfigError('IPoE IPv6 deletate-prefix requires IPv6 prefix to be configured!')
 
     return None
 
 
 def generate(ipoe):
     if not ipoe:
         return None
 
-    render(ipoe_conf, 'accel-ppp/ipoe.config.tmpl', ipoe, trim_blocks=True)
+    render(ipoe_conf, 'accel-ppp/ipoe.config.tmpl', ipoe)
 
     if ipoe['auth_mode'] == 'local':
         render(ipoe_chap_secrets, 'accel-ppp/chap-secrets.ipoe.tmpl', ipoe)
         os.chmod(ipoe_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)
 
     else:
         if os.path.exists(ipoe_chap_secrets):
              os.unlink(ipoe_chap_secrets)
 
     return None
 
 
 def apply(ipoe):
     if ipoe == None:
         call('systemctl stop accel-ppp@ipoe.service')
         for file in [ipoe_conf, ipoe_chap_secrets]:
             if os.path.exists(file):
                 os.unlink(file)
 
         return None
 
     call('systemctl restart accel-ppp@ipoe.service')
 
 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_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index 2260b3fe1..9fbd531da 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -1,107 +1,107 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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.config import Config
 from vyos.configdict import get_accel_dict
 from vyos.configverify import verify_accel_ppp_base_service
 from vyos.template import render
 from vyos.util import call
 from vyos.util import dict_search
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 pppoe_conf = r'/run/accel-pppd/pppoe.conf'
 pppoe_chap_secrets = r'/run/accel-pppd/pppoe.chap-secrets'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['service', 'pppoe-server']
     if not conf.exists(base):
         return None
 
     # retrieve common dictionary keys
     pppoe = get_accel_dict(conf, base, pppoe_chap_secrets)
     return pppoe
 
 def verify(pppoe):
     if not pppoe:
         return None
 
     verify_accel_ppp_base_service(pppoe)
 
     if 'wins_server' in pppoe and len(pppoe['wins_server']) > 2:
         raise ConfigError('Not more then two IPv4 WINS name-servers can be configured')
 
     if 'interface' not in pppoe:
         raise ConfigError('At least one listen interface must be defined!')
 
     # local ippool and gateway settings config checks
     if not (dict_search('client_ip_pool.subnet', pppoe) or
            (dict_search('client_ip_pool.start', pppoe) and
             dict_search('client_ip_pool.stop', pppoe))):
         print('Warning: No PPPoE client pool defined')
 
     if dict_search('authentication.radius.dynamic_author.server', pppoe):
         if not dict_search('authentication.radius.dynamic_author.key', pppoe):
             raise ConfigError('DA/CoE server key required!')
 
     return None
 
 
 def generate(pppoe):
     if not pppoe:
         return None
 
-    render(pppoe_conf, 'accel-ppp/pppoe.config.tmpl', pppoe, trim_blocks=True)
+    render(pppoe_conf, 'accel-ppp/pppoe.config.tmpl', pppoe)
 
     if dict_search('authentication.mode', pppoe) == 'local':
         render(pppoe_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl',
-               pppoe, trim_blocks=True, permission=0o640)
+               pppoe, permission=0o640)
     else:
         if os.path.exists(pppoe_chap_secrets):
             os.unlink(pppoe_chap_secrets)
 
     return None
 
 
 def apply(pppoe):
     if not pppoe:
         call('systemctl stop accel-ppp@pppoe.service')
         for file in [pppoe_conf, pppoe_chap_secrets]:
             if os.path.exists(file):
                 os.unlink(file)
 
         return None
 
     call('systemctl restart accel-ppp@pppoe.service')
 
 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_router-advert.py b/src/conf_mode/service_router-advert.py
index 687d7068f..65eb11ce3 100755
--- a/src/conf_mode/service_router-advert.py
+++ b/src/conf_mode/service_router-advert.py
@@ -1,120 +1,120 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-2019 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.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
 from vyos import airbag
 airbag.enable()
 
 config_file = r'/run/radvd/radvd.conf'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['service', 'router-advert']
     rtradv = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
 
     # We have gathered the dict representation of the CLI, but there are default
     # options which we need to update into the dictionary retrived.
     default_interface_values = defaults(base + ['interface'])
     # we deal with prefix defaults later on
     if 'prefix' in default_interface_values:
         del default_interface_values['prefix']
 
     default_prefix_values = defaults(base + ['interface', 'prefix'])
 
     if 'interface' in rtradv:
         for interface in rtradv['interface']:
             rtradv['interface'][interface] = dict_merge(
                 default_interface_values, rtradv['interface'][interface])
 
             if 'prefix' in rtradv['interface'][interface]:
                 for prefix in rtradv['interface'][interface]['prefix']:
                     rtradv['interface'][interface]['prefix'][prefix] = dict_merge(
                         default_prefix_values, rtradv['interface'][interface]['prefix'][prefix])
 
             if 'name_server' in rtradv['interface'][interface]:
                 # always use a list when dealing with nameservers - eases the template generation
                 if isinstance(rtradv['interface'][interface]['name_server'], str):
                     rtradv['interface'][interface]['name_server'] = [
                         rtradv['interface'][interface]['name_server']]
 
     return rtradv
 
 def verify(rtradv):
     if not rtradv:
         return None
 
     if 'interface' not in rtradv:
         return None
 
     for interface in rtradv['interface']:
         interface = rtradv['interface'][interface]
         if 'prefix' in interface:
             for prefix in interface['prefix']:
                 prefix = interface['prefix'][prefix]
                 valid_lifetime = prefix['valid_lifetime']
                 if valid_lifetime == 'infinity':
                     valid_lifetime = 4294967295
 
                 preferred_lifetime = prefix['preferred_lifetime']
                 if preferred_lifetime == 'infinity':
                     preferred_lifetime = 4294967295
 
                 if not (int(valid_lifetime) > int(preferred_lifetime)):
                     raise ConfigError('Prefix valid-lifetime must be greater then preferred-lifetime')
 
     return None
 
 def generate(rtradv):
     if not rtradv:
         return None
 
-    render(config_file, 'router-advert/radvd.conf.tmpl', rtradv, trim_blocks=True, permission=0o644)
+    render(config_file, 'router-advert/radvd.conf.tmpl', rtradv, permission=0o644)
     return None
 
 def apply(rtradv):
     if not rtradv:
         # bail out early - looks like removal from running config
         call('systemctl stop radvd.service')
         if os.path.exists(config_file):
             os.unlink(config_file)
 
         return None
 
     call('systemctl restart radvd.service')
 
     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/ssh.py b/src/conf_mode/ssh.py
index e07745963..8f99053d2 100755
--- a/src/conf_mode/ssh.py
+++ b/src/conf_mode/ssh.py
@@ -1,95 +1,95 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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.config import Config
 from vyos.configdict import dict_merge
 from vyos.configverify import verify_vrf
 from vyos.util import call
 from vyos.template import render
 from vyos.xml import defaults
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 config_file = r'/run/ssh/sshd_config'
 systemd_override = r'/etc/systemd/system/ssh.service.d/override.conf'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['service', 'ssh']
     if not conf.exists(base):
         return None
 
     ssh = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
     # 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)
     ssh = dict_merge(default_values, ssh)
     # pass config file path - used in override template
     ssh['config_file'] = config_file
 
     return ssh
 
 def verify(ssh):
     if not ssh:
         return None
 
     verify_vrf(ssh)
     return None
 
 def generate(ssh):
     if not ssh:
         if os.path.isfile(config_file):
             os.unlink(config_file)
         if os.path.isfile(systemd_override):
             os.unlink(systemd_override)
 
         return None
 
-    render(config_file, 'ssh/sshd_config.tmpl', ssh, trim_blocks=True)
-    render(systemd_override, 'ssh/override.conf.tmpl', ssh, trim_blocks=True)
+    render(config_file, 'ssh/sshd_config.tmpl', ssh)
+    render(systemd_override, 'ssh/override.conf.tmpl', ssh)
 
     return None
 
 def apply(ssh):
     if not ssh:
         # SSH access is removed in the commit
         call('systemctl stop ssh.service')
 
     # Reload systemd manager configuration
     call('systemctl daemon-reload')
 
     if ssh:
         call('systemctl restart ssh.service')
 
     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/system-login.py b/src/conf_mode/system-login.py
index 2c0bbd4f7..39bad717d 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -1,406 +1,406 @@
 #!/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 crypt import crypt, METHOD_SHA512
 from netifaces import interfaces
 from psutil import users
 from pwd import getpwall, getpwnam
 from spwd import getspnam
 from sys import exit
 
 from vyos.config import Config
 from vyos.template import render
 from vyos.util import cmd, call, DEVNULL, chmod_600, chmod_755
 from vyos import ConfigError
 
 from vyos import airbag
 airbag.enable()
 
 radius_config_file = "/etc/pam_radius_auth.conf"
 
 default_config_data = {
     'deleted': False,
     'add_users': [],
     'del_users': [],
     'radius_server': [],
     'radius_source_address': '',
     'radius_vrf': ''
 }
 
 
 def get_local_users():
     """Return list of dynamically allocated users (see Debian Policy Manual)"""
     local_users = []
     for p in getpwall():
         username = p[0]
         uid = getpwnam(username).pw_uid
         if uid in range(1000, 29999):
             if username not in ['radius_user', 'radius_priv_user']:
                 local_users.append(username)
 
     return local_users
 
 
 def get_config(config=None):
     login = default_config_data
     if config:
         conf = config
     else:
         conf = Config()
     base_level = ['system', 'login']
 
     # We do not need to check if the nodes exist or not and bail out early
     # ... this would interrupt the following logic on determine which users
     # should be deleted and which users should stay.
     #
     # All fine so far!
 
     # Read in all local users and store to list
     for username in conf.list_nodes(base_level + ['user']):
         user = {
             'name': username,
             'password_plaintext': '',
             'password_encrypted': '!',
             'public_keys': [],
             'full_name': '',
             'home_dir': '/home/' + username,
         }
         conf.set_level(base_level + ['user', username])
 
         # Plaintext password
         if conf.exists(['authentication', 'plaintext-password']):
             user['password_plaintext'] = conf.return_value(
                 ['authentication', 'plaintext-password'])
 
         # Encrypted password
         if conf.exists(['authentication', 'encrypted-password']):
             user['password_encrypted'] = conf.return_value(
                 ['authentication', 'encrypted-password'])
 
         # User real name
         if conf.exists(['full-name']):
             user['full_name'] = conf.return_value(['full-name'])
 
         # User home-directory
         if conf.exists(['home-directory']):
             user['home_dir'] = conf.return_value(['home-directory'])
 
         # Read in public keys
         for id in conf.list_nodes(['authentication', 'public-keys']):
             key = {
                 'name': id,
                 'key': '',
                 'options': '',
                 'type': ''
             }
             conf.set_level(base_level + ['user', username, 'authentication',
                                          'public-keys', id])
 
             # Public Key portion
             if conf.exists(['key']):
                 key['key'] = conf.return_value(['key'])
 
             # Options for individual public key
             if conf.exists(['options']):
                 key['options'] = conf.return_value(['options'])
 
             # Type of public key
             if conf.exists(['type']):
                 key['type'] = conf.return_value(['type'])
 
             # Append individual public key to list of user keys
             user['public_keys'].append(key)
 
         login['add_users'].append(user)
 
     #
     # RADIUS configuration
     #
     conf.set_level(base_level + ['radius'])
 
     if conf.exists(['source-address']):
         login['radius_source_address'] = conf.return_value(['source-address'])
 
     # retrieve VRF instance
     if conf.exists(['vrf']):
         login['radius_vrf'] = conf.return_value(['vrf'])
 
     # Read in all RADIUS servers and store to list
     for server in conf.list_nodes(['server']):
         server_cfg = {
             'address': server,
             'disabled': False,
             'key': '',
             'port': '1812',
             'timeout': '2',
             'priority': 255
         }
         conf.set_level(base_level + ['radius', 'server', server])
 
         # Check if RADIUS server was temporary disabled
         if conf.exists(['disable']):
             server_cfg['disabled'] = True
 
         # RADIUS shared secret
         if conf.exists(['key']):
             server_cfg['key'] = conf.return_value(['key'])
 
         # RADIUS authentication port
         if conf.exists(['port']):
             server_cfg['port'] = conf.return_value(['port'])
 
         # RADIUS session timeout
         if conf.exists(['timeout']):
             server_cfg['timeout'] = conf.return_value(['timeout'])
 
         # Check if RADIUS server has priority
         if conf.exists(['priority']):
             server_cfg['priority'] = int(conf.return_value(['priority']))
 
         # Append individual RADIUS server configuration to global server list
         login['radius_server'].append(server_cfg)
 
     # users no longer existing in the running configuration need to be deleted
     local_users = get_local_users()
     cli_users = [tmp['name'] for tmp in login['add_users']]
     # create a list of all users, cli and users
     all_users = list(set(local_users+cli_users))
 
     # Remove any normal users that dos not exist in the current configuration.
     # This can happen if user is added but configuration was not saved and
     # system is rebooted.
     login['del_users'] = [tmp for tmp in all_users if tmp not in cli_users]
 
     return login
 
 
 def verify(login):
     cur_user = os.environ['SUDO_USER']
     if cur_user in login['del_users']:
         raise ConfigError(
             'Attempting to delete current user: {}'.format(cur_user))
 
     for user in login['add_users']:
         for key in user['public_keys']:
             if not key['type']:
                 raise ConfigError(
                     'SSH public key type missing for "{name}"!'.format(**key))
 
             if not key['key']:
                 raise ConfigError(
                     'SSH public key for id "{name}" missing!'.format(**key))
 
     # At lease one RADIUS server must not be disabled
     if len(login['radius_server']) > 0:
         fail = True
         for server in login['radius_server']:
             if not server['disabled']:
                 fail = False
         if fail:
             raise ConfigError('At least one RADIUS server must be active.')
 
     vrf_name = login['radius_vrf']
     if vrf_name and vrf_name not in interfaces():
         raise ConfigError(f'VRF "{vrf_name}" does not exist')
 
     return None
 
 
 def generate(login):
     # calculate users encrypted password
     for user in login['add_users']:
         if user['password_plaintext']:
             user['password_encrypted'] = crypt(
                 user['password_plaintext'], METHOD_SHA512)
             user['password_plaintext'] = ''
 
             # remove old plaintext password and set new encrypted password
             env = os.environ.copy()
             env['vyos_libexec_dir'] = '/usr/libexec/vyos'
 
             call("/opt/vyatta/sbin/my_delete system login user '{name}' "
                  "authentication plaintext-password"
                  .format(**user), env=env)
 
             call("/opt/vyatta/sbin/my_set system login user '{name}' "
                  "authentication encrypted-password '{password_encrypted}'"
                  .format(**user), env=env)
 
         else:
             try:
                 if getspnam(user['name']).sp_pwdp == user['password_encrypted']:
                     # If the current encrypted bassword matches the encrypted password
                     # from the config - do not update it. This will remove the encrypted
                     # value from the system logs.
                     #
                     # The encrypted password will be set only once during the first boot
                     # after an image upgrade.
                     user['password_encrypted'] = ''
             except:
                 pass
 
     if len(login['radius_server']) > 0:
         render(radius_config_file, 'system-login/pam_radius_auth.conf.tmpl',
-               login, trim_blocks=True)
+               login)
 
         uid = getpwnam('root').pw_uid
         gid = getpwnam('root').pw_gid
         os.chown(radius_config_file, uid, gid)
         chmod_600(radius_config_file)
     else:
         if os.path.isfile(radius_config_file):
             os.unlink(radius_config_file)
 
     return None
 
 
 def apply(login):
     for user in login['add_users']:
         # make new user using vyatta shell and make home directory (-m),
         # default group of 100 (users)
         command = "useradd -m -N"
         # check if user already exists:
         if user['name'] in get_local_users():
             # update existing account
             command = "usermod"
 
         # all accounts use /bin/vbash
         command += " -s /bin/vbash"
         # we need to use '' quotes when passing formatted data to the shell
         # else it will not work as some data parts are lost in translation
         if user['password_encrypted']:
             command += " -p '{}'".format(user['password_encrypted'])
 
         if user['full_name']:
             command += " -c '{}'".format(user['full_name'])
 
         if user['home_dir']:
             command += " -d '{}'".format(user['home_dir'])
 
         command += " -G frrvty,vyattacfg,sudo,adm,dip,disk"
         command += " {}".format(user['name'])
 
         try:
             cmd(command)
 
             uid = getpwnam(user['name']).pw_uid
             gid = getpwnam(user['name']).pw_gid
 
             # we should not rely on the value stored in user['home_dir'], as a
             # crazy user will choose username root or any other system user
             # which will fail. Should we deny using root at all?
             home_dir = getpwnam(user['name']).pw_dir
 
             # install ssh keys
             ssh_key_dir = home_dir + '/.ssh'
             if not os.path.isdir(ssh_key_dir):
                 os.mkdir(ssh_key_dir)
                 os.chown(ssh_key_dir, uid, gid)
                 chmod_755(ssh_key_dir)
 
             ssh_key_file = ssh_key_dir + '/authorized_keys'
             with open(ssh_key_file, 'w') as f:
                 f.write("# Automatically generated by VyOS\n")
                 f.write("# Do not edit, all changes will be lost\n")
 
                 for id in user['public_keys']:
                     line = ''
                     if id['options']:
                         line = '{} '.format(id['options'])
 
                     line += '{} {} {}\n'.format(id['type'],
                                                 id['key'], id['name'])
                     f.write(line)
 
             os.chown(ssh_key_file, uid, gid)
             chmod_600(ssh_key_file)
 
         except Exception as e:
             print(e)
             raise ConfigError('Adding user "{name}" raised exception'
                               .format(**user))
 
     for user in login['del_users']:
         try:
             # Logout user if he is logged in
             if user in list(set([tmp[0] for tmp in users()])):
                 print('{} is logged in, forcing logout'.format(user))
                 call('pkill -HUP -u {}'.format(user))
 
             # Remove user account but leave home directory to be safe
             call(f'userdel -r {user}', stderr=DEVNULL)
 
         except Exception as e:
             raise ConfigError(f'Deleting user "{user}" raised exception: {e}')
 
     #
     # RADIUS configuration
     #
     if len(login['radius_server']) > 0:
         try:
             env = os.environ.copy()
             env['DEBIAN_FRONTEND'] = 'noninteractive'
             # Enable RADIUS in PAM
             cmd("pam-auth-update --package --enable radius", env=env)
 
             # Make NSS system aware of RADIUS, too
             command = "sed -i -e \'/\smapname/b\' \
                           -e \'/^passwd:/s/\s\s*/&mapuid /\' \
                           -e \'/^passwd:.*#/s/#.*/mapname &/\' \
                           -e \'/^passwd:[^#]*$/s/$/ mapname &/\' \
                           -e \'/^group:.*#/s/#.*/ mapname &/\' \
                           -e \'/^group:[^#]*$/s/: */&mapname /\' \
                           /etc/nsswitch.conf"
 
             cmd(command)
 
         except Exception as e:
             raise ConfigError('RADIUS configuration failed: {}'.format(e))
 
     else:
         try:
             env = os.environ.copy()
             env['DEBIAN_FRONTEND'] = 'noninteractive'
 
             # Disable RADIUS in PAM
             cmd("pam-auth-update --package --remove radius", env=env)
 
             command = "sed -i -e \'/^passwd:.*mapuid[ \t]/s/mapuid[ \t]//\' \
                    -e \'/^passwd:.*[ \t]mapname/s/[ \t]mapname//\' \
                    -e \'/^group:.*[ \t]mapname/s/[ \t]mapname//\' \
                    -e \'s/[ \t]*$//\' \
                    /etc/nsswitch.conf"
 
             cmd(command)
 
         except Exception as e:
             raise ConfigError(
                 'Removing RADIUS configuration failed.\n{}'.format(e))
 
     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/system-option.py b/src/conf_mode/system-option.py
index 2376e5d44..447c97a78 100755
--- a/src/conf_mode/system-option.py
+++ b/src/conf_mode/system-option.py
@@ -1,136 +1,136 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-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 netifaces import interfaces
 from sys import exit
 from time import sleep
 
 from vyos.config import Config
 from vyos.configdict import dict_merge
 from vyos.template import render
 from vyos.util import cmd
 from vyos.validate import is_addr_assigned
 from vyos.xml import defaults
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 curlrc_config = r'/etc/curlrc'
 ssh_config = r'/etc/ssh/ssh_config'
 systemd_action_file = '/lib/systemd/system/ctrl-alt-del.target'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['system', 'option']
     options = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
 
     # 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)
     options = dict_merge(default_values, options)
 
     return options
 
 def verify(options):
     if 'http_client' in options:
         config = options['http_client']
         if 'source_interface' in config:
             if not config['source_interface'] in interfaces():
                 raise ConfigError(f'Source interface {source_interface} does not '
                                   f'exist'.format(**config))
 
         if {'source_address', 'source_interface'} <= set(config):
             raise ConfigError('Can not define both HTTP source-interface and source-address')
 
         if 'source_address' in config:
             if not is_addr_assigned(config['source_address']):
                 raise ConfigError('No interface with give address specified!')
 
     if 'ssh_client' in options:
         config = options['ssh_client']
         if 'source_address' in config:
             if not is_addr_assigned(config['source_address']):
                 raise ConfigError('No interface with give address specified!')
 
     return None
 
 def generate(options):
-    render(curlrc_config, 'system/curlrc.tmpl', options, trim_blocks=True)
-    render(ssh_config, 'system/ssh_config.tmpl', options, trim_blocks=True)
+    render(curlrc_config, 'system/curlrc.tmpl', options)
+    render(ssh_config, 'system/ssh_config.tmpl', options)
     return None
 
 def apply(options):
     # System bootup beep
     if 'startup_beep' in options:
         cmd('systemctl enable vyos-beep.service')
     else:
         cmd('systemctl disable vyos-beep.service')
 
     # Ctrl-Alt-Delete action
     if os.path.exists(systemd_action_file):
         os.unlink(systemd_action_file)
     if 'ctrl_alt_del' in options:
         if options['ctrl_alt_del'] == 'reboot':
             os.symlink('/lib/systemd/system/reboot.target', systemd_action_file)
         elif options['ctrl_alt_del'] == 'poweroff':
             os.symlink('/lib/systemd/system/poweroff.target', systemd_action_file)
 
     # Configure HTTP client
     if 'http_client' not in options:
         if os.path.exists(curlrc_config):
             os.unlink(curlrc_config)
 
     # Configure SSH client
     if 'ssh_client' not in options:
         if os.path.exists(ssh_config):
             os.unlink(ssh_config)
 
     # Reboot system on kernel panic
     with open('/proc/sys/kernel/panic', 'w') as f:
         if 'reboot_on_panic' in options:
             f.write('60')
         else:
             f.write('0')
 
     # tuned - performance tuning
     if 'performance' in options:
         cmd('systemctl restart tuned.service')
         # wait until daemon has started before sending configuration
         while (int(os.system('systemctl is-active --quiet tuned.service')) != 0):
             sleep(0.250)
         cmd('tuned-adm profile network-{performance}'.format(**options))
     else:
         cmd('systemctl stop tuned.service')
 
     # Keyboard layout - there will be always the default key inside the dict
     # but we check for key existence anyway
     if 'keyboard_layout' in options:
         cmd('loadkeys -C /dev/console {keyboard_layout}'.format(**options))
 
 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/system-syslog.py b/src/conf_mode/system-syslog.py
index b1daf7a82..3d8a51cd8 100755
--- a/src/conf_mode/system-syslog.py
+++ b/src/conf_mode/system-syslog.py
@@ -1,268 +1,268 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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 re
 
 from sys import exit
 
 from vyos.config import Config
 from vyos import ConfigError
 from vyos.util import run
 from vyos.template import render
 
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         c = config
     else:
         c = Config()
     if not c.exists('system syslog'):
         return None
     c.set_level('system syslog')
 
     config_data = {
         'files': {},
       'console': {},
       'hosts': {},
       'user': {}
     }
 
     #
     # /etc/rsyslog.d/vyos-rsyslog.conf
     # 'set system syslog global'
     #
     config_data['files'].update(
         {
             'global': {
                 'log-file': '/var/log/messages',
                 'max-size': 262144,
                 'action-on-max-size': '/usr/sbin/logrotate /etc/logrotate.d/vyos-rsyslog',
                 'selectors': '*.notice;local7.debug',
                 'max-files': '5',
                 'preserver_fqdn': False
             }
         }
     )
 
     if c.exists('global marker'):
         config_data['files']['global']['marker'] = True
         if c.exists('global marker interval'):
             config_data['files']['global'][
                 'marker-interval'] = c.return_value('global marker interval')
     if c.exists('global facility'):
         config_data['files']['global'][
             'selectors'] = generate_selectors(c, 'global facility')
     if c.exists('global archive size'):
         config_data['files']['global']['max-size'] = int(
             c.return_value('global archive size')) * 1024
     if c.exists('global archive file'):
         config_data['files']['global'][
             'max-files'] = c.return_value('global archive file')
     if c.exists('global preserve-fqdn'):
         config_data['files']['global']['preserver_fqdn'] = True
 
     #
     # set system syslog file
     #
 
     if c.exists('file'):
         filenames = c.list_nodes('file')
         for filename in filenames:
             config_data['files'].update(
                 {
                     filename: {
                         'log-file': '/var/log/user/' + filename,
                         'max-files': '5',
                         'action-on-max-size': '/usr/sbin/logrotate /etc/logrotate.d/' + filename,
                         'selectors': '*.err',
                         'max-size': 262144
                     }
                 }
             )
 
             if c.exists('file ' + filename + ' facility'):
                 config_data['files'][filename]['selectors'] = generate_selectors(
                     c, 'file ' + filename + ' facility')
             if c.exists('file ' + filename + ' archive size'):
                 config_data['files'][filename]['max-size'] = int(
                     c.return_value('file ' + filename + ' archive size')) * 1024
             if c.exists('file ' + filename + ' archive files'):
                 config_data['files'][filename]['max-files'] = c.return_value(
                     'file ' + filename + ' archive files')
 
     # set system syslog console
     if c.exists('console'):
         config_data['console'] = {
             '/dev/console': {
                 'selectors': '*.err'
             }
         }
 
     for f in c.list_nodes('console facility'):
         if c.exists('console facility ' + f + ' level'):
             config_data['console'] = {
                 '/dev/console': {
                     'selectors': generate_selectors(c, 'console facility')
                 }
             }
 
     # set system syslog host
     if c.exists('host'):
         rhosts = c.list_nodes('host')
         proto = 'udp'
         for rhost in rhosts:
             for fac in c.list_nodes('host ' + rhost + ' facility'):
                 if c.exists('host ' + rhost + ' facility ' + fac + ' protocol'):
                     proto = c.return_value(
                         'host ' + rhost + ' facility ' + fac + ' protocol')
                 else:
                     proto = 'udp'
 
             config_data['hosts'].update(
                 {
                     rhost: {
                         'selectors': generate_selectors(c, 'host ' + rhost + ' facility'),
                         'proto': proto
                     }
                 }
             )
             if c.exists('host ' + rhost + ' port'):
                 config_data['hosts'][rhost][
                     'port'] = c.return_value(['host', rhost, 'port'])
 
             # set system syslog host x.x.x.x format octet-counted
             if c.exists('host ' + rhost + ' format octet-counted'):
                 config_data['hosts'][rhost]['oct_count'] = True
             else:
                 config_data['hosts'][rhost]['oct_count'] = False
 
     # set system syslog user
     if c.exists('user'):
         usrs = c.list_nodes('user')
         for usr in usrs:
             config_data['user'].update(
                 {
                     usr: {
                         'selectors': generate_selectors(c, 'user ' + usr + ' facility')
                     }
                 }
             )
 
     return config_data
 
 
 def generate_selectors(c, config_node):
 # protocols and security are being mapped here
 # for backward compatibility with old configs
 # security and protocol mappings can be removed later
     nodes = c.list_nodes(config_node)
     selectors = ""
     for node in nodes:
         lvl = c.return_value(config_node + ' ' + node + ' level')
         if lvl == None:
             lvl = "err"
         if lvl == 'all':
             lvl = '*'
         if node == 'all' and node != nodes[-1]:
             selectors += "*." + lvl + ";"
         elif node == 'all':
             selectors += "*." + lvl
         elif node != nodes[-1]:
             if node == 'protocols':
                 node = 'local7'
             if node == 'security':
                 node = 'auth'
             selectors += node + "." + lvl + ";"
         else:
             if node == 'protocols':
                 node = 'local7'
             if node == 'security':
                 node = 'auth'
             selectors += node + "." + lvl
     return selectors
 
 
 def generate(c):
     if c == None:
         return None
 
     conf = '/etc/rsyslog.d/vyos-rsyslog.conf'
-    render(conf, 'syslog/rsyslog.conf.tmpl', c, trim_blocks=True)
+    render(conf, 'syslog/rsyslog.conf.tmpl', c)
 
     # eventually write for each file its own logrotate file, since size is
     # defined it shouldn't matter
     conf = '/etc/logrotate.d/vyos-rsyslog'
-    render(conf, 'syslog/logrotate.tmpl', c, trim_blocks=True)
+    render(conf, 'syslog/logrotate.tmpl', c)
 
 
 def verify(c):
     if c == None:
         return None
 
     # may be obsolete
     # /etc/rsyslog.conf is generated somewhere and copied over the original (exists in /opt/vyatta/etc/rsyslog.conf)
     # it interferes with the global logging, to make sure we are using a single base, template is enforced here
     #
     if not os.path.islink('/etc/rsyslog.conf'):
         os.remove('/etc/rsyslog.conf')
         os.symlink(
             '/usr/share/vyos/templates/rsyslog/rsyslog.conf', '/etc/rsyslog.conf')
 
     # /var/log/vyos-rsyslog were the old files, we may want to clean those up, but currently there
     # is a chance that someone still needs it, so I don't automatically remove
     # them
     #
 
     if c == None:
         return None
 
     fac = [
         '*', 'auth', 'authpriv', 'cron', 'daemon', 'kern', 'lpr', 'mail', 'mark', 'news', 'protocols', 'security',
           'syslog', 'user', 'uucp', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7']
     lvl = ['emerg', 'alert', 'crit', 'err',
            'warning', 'notice', 'info', 'debug', '*']
 
     for conf in c:
         if c[conf]:
             for item in c[conf]:
                 for s in c[conf][item]['selectors'].split(";"):
                     f = re.sub("\..*$", "", s)
                     if f not in fac:
                         raise ConfigError(
                             'Invalid facility ' + s + ' set in ' + conf + ' ' + item)
                     l = re.sub("^.+\.", "", s)
                     if l not in lvl:
                         raise ConfigError(
                             'Invalid logging level ' + s + ' set in ' + conf + ' ' + item)
 
 
 def apply(c):
     if not c:
         return run('systemctl stop syslog.service')
     return run('systemctl restart syslog.service')
 
 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/system_lcd.py b/src/conf_mode/system_lcd.py
index a540d1b9e..b5ce32beb 100755
--- a/src/conf_mode/system_lcd.py
+++ b/src/conf_mode/system_lcd.py
@@ -1,91 +1,91 @@
 #!/usr/bin/env python3
 #
 # Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io>
 #
 # 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.config import Config
 from vyos.util import call
 from vyos.util import find_device_file
 from vyos.template import render
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 lcdd_conf = '/run/LCDd/LCDd.conf'
 lcdproc_conf = '/run/lcdproc/lcdproc.conf'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['system', 'lcd']
     lcd = conf.get_config_dict(base, key_mangling=('-', '_'),
                                get_first_key=True)
     # Return (possibly empty) dictionary
     return lcd
 
 def verify(lcd):
     if not lcd:
         return None
 
     if 'model' in lcd and lcd['model'] in ['sdec']:
         # This is a fixed LCD display, no device needed - bail out early
         return None
 
     if not {'device', 'model'} <= set(lcd):
         raise ConfigError('Both device and driver must be set!')
 
     return None
 
 def generate(lcd):
     if not lcd:
         return None
 
     if 'device' in lcd:
         lcd['device'] = find_device_file(lcd['device'])
 
     # Render config file for daemon LCDd
-    render(lcdd_conf, 'lcd/LCDd.conf.tmpl', lcd, trim_blocks=True)
+    render(lcdd_conf, 'lcd/LCDd.conf.tmpl', lcd)
     # Render config file for client lcdproc
-    render(lcdproc_conf, 'lcd/lcdproc.conf.tmpl', lcd, trim_blocks=True)
+    render(lcdproc_conf, 'lcd/lcdproc.conf.tmpl', lcd)
 
     return None
 
 def apply(lcd):
     if not lcd:
         call('systemctl stop lcdproc.service LCDd.service')
 
         for file in [lcdd_conf, lcdproc_conf]:
             if os.path.exists(file):
                 os.remove(file)
     else:
         # Restart server
         call('systemctl restart LCDd.service lcdproc.service')
 
     return None
 
 if __name__ == '__main__':
     try:
         config_dict = get_config()
         verify(config_dict)
         generate(config_dict)
         apply(config_dict)
     except ConfigError as e:
         print(e)
         exit(1)
diff --git a/src/conf_mode/tftp_server.py b/src/conf_mode/tftp_server.py
index 56e195b6a..2409eec1f 100755
--- a/src/conf_mode/tftp_server.py
+++ b/src/conf_mode/tftp_server.py
@@ -1,140 +1,140 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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 stat
 import pwd
 
 from copy import deepcopy
 from glob import glob
 from sys import exit
 
 from vyos.config import Config
 from vyos.configdict import dict_merge
 from vyos.template import render
 from vyos.template import is_ipv4
 from vyos.util import call
 from vyos.util import chmod_755
 from vyos.validate import is_addr_assigned
 from vyos.xml import defaults
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 config_file = r'/etc/default/tftpd'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     base = ['service', 'tftp-server']
     if not conf.exists(base):
         return None
 
     tftpd = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
     # 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)
     tftpd = dict_merge(default_values, tftpd)
     return tftpd
 
 def verify(tftpd):
     # bail out early - looks like removal from running config
     if not tftpd:
         return None
 
     # Configuring allowed clients without a server makes no sense
     if 'directory' not in tftpd:
         raise ConfigError('TFTP root directory must be configured!')
 
     if 'listen_address' not in tftpd:
         raise ConfigError('TFTP server listen address must be configured!')
 
     for address in tftpd['listen_address']:
         if not is_addr_assigned(address):
             print(f'WARNING: TFTP server listen address "{address}" not ' \
                   'assigned to any interface!')
 
     return None
 
 def generate(tftpd):
     # cleanup any available configuration file
     # files will be recreated on demand
     for i in glob(config_file + '*'):
         os.unlink(i)
 
     # bail out early - looks like removal from running config
     if tftpd is None:
         return None
 
     idx = 0
     for address in tftpd['listen_address']:
         config = deepcopy(tftpd)
         port = tftpd['port']
         if is_ipv4(address):
             config['listen_address'] = f'{address}:{port} -4'
         else:
             config['listen_address'] = f'[{address}]:{port} -6'
 
         file = config_file + str(idx)
-        render(file, 'tftp-server/default.tmpl', config, trim_blocks=True)
+        render(file, 'tftp-server/default.tmpl', config)
         idx = idx + 1
 
     return None
 
 def apply(tftpd):
     # stop all services first - then we will decide
     call('systemctl stop tftpd@*.service')
 
     # bail out early - e.g. service deletion
     if tftpd is None:
         return None
 
     tftp_root = tftpd['directory']
     if not os.path.exists(tftp_root):
         os.makedirs(tftp_root)
         chmod_755(tftp_root)
 
     # get UNIX uid for user 'tftp'
     tftp_uid = pwd.getpwnam('tftp').pw_uid
     tftp_gid = pwd.getpwnam('tftp').pw_gid
 
     # get UNIX uid for tftproot directory
     dir_uid = os.stat(tftp_root).st_uid
     dir_gid = os.stat(tftp_root).st_gid
 
     # adjust uid/gid of tftproot directory if files don't belong to user tftp
     if (tftp_uid != dir_uid) or (tftp_gid != dir_gid):
         os.chown(tftp_root, tftp_uid, tftp_gid)
 
     idx = 0
     for address in tftpd['listen_address']:
         call(f'systemctl restart tftpd@{idx}.service')
         idx = idx + 1
 
     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/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py
index 80eb8daf2..e970d2ef5 100755
--- a/src/conf_mode/vpn_l2tp.py
+++ b/src/conf_mode/vpn_l2tp.py
@@ -1,392 +1,392 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-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 re
 
 from copy import deepcopy
 from stat import S_IRUSR, S_IWUSR, S_IRGRP
 from sys import exit
 from time import sleep
 
 from ipaddress import ip_network
 
 from vyos.config import Config
 from vyos.template import is_ipv4
 from vyos.template import render
 from vyos.util import call, get_half_cpus
 from vyos import ConfigError
 
 from vyos import airbag
 airbag.enable()
 
 l2tp_conf = '/run/accel-pppd/l2tp.conf'
 l2tp_chap_secrets = '/run/accel-pppd/l2tp.chap-secrets'
 
 default_config_data = {
     'auth_mode': 'local',
     'auth_ppp_mppe': 'prefer',
     'auth_proto': ['auth_mschap_v2'],
     'chap_secrets_file': l2tp_chap_secrets, # used in Jinja2 template
     'client_ip_pool': None,
     'client_ip_subnets': [],
     'client_ipv6_pool': [],
     'client_ipv6_delegate_prefix': [],
     'dnsv4': [],
     'dnsv6': [],
     'gateway_address': '10.255.255.0',
     'local_users' : [],
     'mtu': '1436',
     'outside_addr': '',
     'ppp_mppe': 'prefer',
     'ppp_echo_failure' : '3',
     'ppp_echo_interval' : '30',
     'ppp_echo_timeout': '0',
     'radius_server': [],
     'radius_acct_inter_jitter': '',
     'radius_acct_tmo': '3',
     'radius_max_try': '3',
     'radius_timeout': '3',
     'radius_nas_id': '',
     'radius_nas_ip': '',
     'radius_source_address': '',
     'radius_shaper_attr': '',
     'radius_shaper_vendor': '',
     'radius_dynamic_author': '',
     'wins': [],
     'ip6_column': [],
     'thread_cnt': get_half_cpus()
 }
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base_path = ['vpn', 'l2tp', 'remote-access']
     if not conf.exists(base_path):
         return None
 
     conf.set_level(base_path)
     l2tp = deepcopy(default_config_data)
 
     ### general options ###
     if conf.exists(['name-server']):
         for name_server in conf.return_values(['name-server']):
             if is_ipv4(name_server):
                 l2tp['dnsv4'].append(name_server)
             else:
                 l2tp['dnsv6'].append(name_server)
 
     if conf.exists(['wins-server']):
         l2tp['wins'] = conf.return_values(['wins-server'])
 
     if conf.exists('outside-address'):
         l2tp['outside_addr'] = conf.return_value('outside-address')
 
     if conf.exists(['authentication', 'mode']):
         l2tp['auth_mode'] = conf.return_value(['authentication', 'mode'])
 
     if conf.exists(['authentication', 'require']):
         l2tp['auth_proto'] = []
         auth_mods = {
             'pap': 'auth_pap',
             'chap': 'auth_chap_md5',
             'mschap': 'auth_mschap_v1',
             'mschap-v2': 'auth_mschap_v2'
         }
 
         for proto in conf.return_values(['authentication', 'require']):
             l2tp['auth_proto'].append(auth_mods[proto])
 
     if conf.exists(['authentication', 'mppe']):
         l2tp['auth_ppp_mppe'] = conf.return_value(['authentication', 'mppe'])
 
     #
     # local auth
     if conf.exists(['authentication', 'local-users']):
         for username in conf.list_nodes(['authentication', 'local-users', 'username']):
             user = {
                 'name' : username,
                 'password' : '',
                 'state' : 'enabled',
                 'ip' : '*',
                 'upload' : None,
                 'download' : None
             }
 
             conf.set_level(base_path + ['authentication', 'local-users', 'username', username])
 
             if conf.exists(['password']):
                 user['password'] = conf.return_value(['password'])
 
             if conf.exists(['disable']):
                 user['state'] = 'disable'
 
             if conf.exists(['static-ip']):
                 user['ip'] = conf.return_value(['static-ip'])
 
             if conf.exists(['rate-limit', 'download']):
                 user['download'] = conf.return_value(['rate-limit', 'download'])
 
             if conf.exists(['rate-limit', 'upload']):
                 user['upload'] = conf.return_value(['rate-limit', 'upload'])
 
             l2tp['local_users'].append(user)
 
     #
     # RADIUS auth and settings
     conf.set_level(base_path + ['authentication', 'radius'])
     if conf.exists(['server']):
         for server in conf.list_nodes(['server']):
             radius = {
                 'server' : server,
                 'key' : '',
                 'fail_time' : 0,
                 'port' : '1812',
                 'acct_port' : '1813'
             }
 
             conf.set_level(base_path + ['authentication', 'radius', 'server', server])
 
             if conf.exists(['disable-accounting']):
                 radius['acct_port'] = '0'
 
             if conf.exists(['fail-time']):
                 radius['fail_time'] = conf.return_value(['fail-time'])
 
             if conf.exists(['port']):
                 radius['port'] = conf.return_value(['port'])
 
             if conf.exists(['acct-port']):
                 radius['acct_port'] = conf.return_value(['acct-port'])
 
             if conf.exists(['key']):
                 radius['key'] = conf.return_value(['key'])
 
             if not conf.exists(['disable']):
                 l2tp['radius_server'].append(radius)
 
         #
         # advanced radius-setting
         conf.set_level(base_path + ['authentication', 'radius'])
 
         if conf.exists(['acct-interim-jitter']):
             l2tp['radius_acct_inter_jitter'] = conf.return_value(['acct-interim-jitter'])
 
         if conf.exists(['acct-timeout']):
             l2tp['radius_acct_tmo'] = conf.return_value(['acct-timeout'])
 
         if conf.exists(['max-try']):
             l2tp['radius_max_try'] = conf.return_value(['max-try'])
 
         if conf.exists(['timeout']):
             l2tp['radius_timeout'] = conf.return_value(['timeout'])
 
         if conf.exists(['nas-identifier']):
             l2tp['radius_nas_id'] = conf.return_value(['nas-identifier'])
 
         if conf.exists(['nas-ip-address']):
             l2tp['radius_nas_ip'] = conf.return_value(['nas-ip-address'])
 
         if conf.exists(['source-address']):
             l2tp['radius_source_address'] = conf.return_value(['source-address'])
 
         # Dynamic Authorization Extensions (DOA)/Change Of Authentication (COA)
         if conf.exists(['dynamic-author']):
             dae = {
                 'port' : '',
                 'server' : '',
                 'key' : ''
             }
 
             if conf.exists(['dynamic-author', 'server']):
                 dae['server'] = conf.return_value(['dynamic-author', 'server'])
 
             if conf.exists(['dynamic-author', 'port']):
                 dae['port'] = conf.return_value(['dynamic-author', 'port'])
 
             if conf.exists(['dynamic-author', 'key']):
                 dae['key'] = conf.return_value(['dynamic-author', 'key'])
 
             l2tp['radius_dynamic_author'] = dae
 
         if conf.exists(['rate-limit', 'enable']):
             l2tp['radius_shaper_attr'] = 'Filter-Id'
             c_attr = ['rate-limit', 'enable', 'attribute']
             if conf.exists(c_attr):
                 l2tp['radius_shaper_attr'] = conf.return_value(c_attr)
 
             c_vendor = ['rate-limit', 'enable', 'vendor']
             if conf.exists(c_vendor):
                 l2tp['radius_shaper_vendor'] = conf.return_value(c_vendor)
 
     conf.set_level(base_path)
     if conf.exists(['client-ip-pool']):
         if conf.exists(['client-ip-pool', 'start']) and conf.exists(['client-ip-pool', 'stop']):
             start = conf.return_value(['client-ip-pool', 'start'])
             stop  = conf.return_value(['client-ip-pool', 'stop'])
             l2tp['client_ip_pool'] = start + '-' + re.search('[0-9]+$', stop).group(0)
 
     if conf.exists(['client-ip-pool', 'subnet']):
         l2tp['client_ip_subnets'] = conf.return_values(['client-ip-pool', 'subnet'])
 
     if conf.exists(['client-ipv6-pool', 'prefix']):
         l2tp['ip6_column'].append('ip6')
         for prefix in conf.list_nodes(['client-ipv6-pool', 'prefix']):
             tmp = {
                 'prefix': prefix,
                 'mask': '64'
             }
 
             if conf.exists(['client-ipv6-pool', 'prefix', prefix, 'mask']):
                 tmp['mask'] = conf.return_value(['client-ipv6-pool', 'prefix', prefix, 'mask'])
 
             l2tp['client_ipv6_pool'].append(tmp)
 
     if conf.exists(['client-ipv6-pool', 'delegate']):
         l2tp['ip6_column'].append('ip6-db')
         for prefix in conf.list_nodes(['client-ipv6-pool', 'delegate']):
             tmp = {
                 'prefix': prefix,
                 'mask': ''
             }
 
             if conf.exists(['client-ipv6-pool', 'delegate', prefix, 'delegation-prefix']):
                 tmp['mask'] = conf.return_value(['client-ipv6-pool', 'delegate', prefix, 'delegation-prefix'])
 
             l2tp['client_ipv6_delegate_prefix'].append(tmp)
 
     if conf.exists(['mtu']):
         l2tp['mtu'] = conf.return_value(['mtu'])
 
     # gateway address
     if conf.exists(['gateway-address']):
         l2tp['gateway_address'] = conf.return_value(['gateway-address'])
     else:
         # calculate gw-ip-address
         if conf.exists(['client-ip-pool', 'start']):
             # use start ip as gw-ip-address
             l2tp['gateway_address'] = conf.return_value(['client-ip-pool', 'start'])
 
         elif conf.exists(['client-ip-pool', 'subnet']):
             # use first ip address from first defined pool
             subnet = conf.return_values(['client-ip-pool', 'subnet'])[0]
             subnet = ip_network(subnet)
             l2tp['gateway_address'] = str(list(subnet.hosts())[0])
 
     # LNS secret
     if conf.exists(['lns', 'shared-secret']):
         l2tp['lns_shared_secret'] = conf.return_value(['lns', 'shared-secret'])
 
     if conf.exists(['ccp-disable']):
         l2tp['ccp_disable'] = True
 
     # PPP options
     if conf.exists(['idle']):
         l2tp['ppp_echo_timeout'] = conf.return_value(['idle'])
 
     if conf.exists(['ppp-options', 'lcp-echo-failure']):
         l2tp['ppp_echo_failure'] = conf.return_value(['ppp-options', 'lcp-echo-failure'])
 
     if conf.exists(['ppp-options', 'lcp-echo-interval']):
         l2tp['ppp_echo_interval'] = conf.return_value(['ppp-options', 'lcp-echo-interval'])
 
     return l2tp
 
 
 def verify(l2tp):
     if not l2tp:
         return None
 
     if l2tp['auth_mode'] == 'local':
         if not l2tp['local_users']:
             raise ConfigError('L2TP local auth mode requires local users to be configured!')
 
         for user in l2tp['local_users']:
             if not user['password']:
                 raise ConfigError(f"Password required for user {user['name']}")
 
     elif l2tp['auth_mode'] == 'radius':
         if len(l2tp['radius_server']) == 0:
             raise ConfigError("RADIUS authentication requires at least one server")
 
         for radius in l2tp['radius_server']:
             if not radius['key']:
                 raise ConfigError(f"Missing RADIUS secret for server { radius['key'] }")
 
     # check for the existence of a client ip pool
     if not (l2tp['client_ip_pool'] or l2tp['client_ip_subnets']):
         raise ConfigError(
             "set vpn l2tp remote-access client-ip-pool requires subnet or start/stop IP pool")
 
     # check ipv6
     if l2tp['client_ipv6_delegate_prefix'] and not l2tp['client_ipv6_pool']:
         raise ConfigError('IPv6 prefix delegation requires client-ipv6-pool prefix')
 
     for prefix in l2tp['client_ipv6_delegate_prefix']:
         if not prefix['mask']:
             raise ConfigError('Delegation-prefix required for individual delegated networks')
 
     if len(l2tp['wins']) > 2:
         raise ConfigError('Not more then two IPv4 WINS name-servers can be configured')
 
     if len(l2tp['dnsv4']) > 2:
         raise ConfigError('Not more then two IPv4 DNS name-servers can be configured')
 
     if len(l2tp['dnsv6']) > 3:
         raise ConfigError('Not more then three IPv6 DNS name-servers can be configured')
 
     return None
 
 
 def generate(l2tp):
     if not l2tp:
         return None
 
-    render(l2tp_conf, 'accel-ppp/l2tp.config.tmpl', l2tp, trim_blocks=True)
+    render(l2tp_conf, 'accel-ppp/l2tp.config.tmpl', l2tp)
 
     if l2tp['auth_mode'] == 'local':
         render(l2tp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', l2tp)
         os.chmod(l2tp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)
 
     else:
         if os.path.exists(l2tp_chap_secrets):
              os.unlink(l2tp_chap_secrets)
 
     return None
 
 
 def apply(l2tp):
     if not l2tp:
         call('systemctl stop accel-ppp@l2tp.service')
         for file in [l2tp_chap_secrets, l2tp_conf]:
             if os.path.exists(file):
                 os.unlink(file)
 
         return None
 
     call('systemctl restart accel-ppp@l2tp.service')
 
 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/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py
index af8604972..b2aa13c0d 100755
--- a/src/conf_mode/vpn_openconnect.py
+++ b/src/conf_mode/vpn_openconnect.py
@@ -1,135 +1,134 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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.config import Config
 from vyos.configdict import dict_merge
 from vyos.xml import defaults
 from vyos.template import render
 from vyos.util import call
 from vyos import ConfigError
 from crypt import crypt, mksalt, METHOD_SHA512
 
 from vyos import airbag
 airbag.enable()
 
 cfg_dir        = '/run/ocserv'
 ocserv_conf    = cfg_dir + '/ocserv.conf'
 ocserv_passwd  = cfg_dir + '/ocpasswd'
 radius_cfg     = cfg_dir + '/radiusclient.conf'
 radius_servers = cfg_dir + '/radius_servers'
 
-
 # Generate hash from user cleartext password
 def get_hash(password):
     return crypt(password, mksalt(METHOD_SHA512))
 
-
 def get_config():
     conf = Config()
     base = ['vpn', 'openconnect']
     if not conf.exists(base):
         return None
 
     ocserv = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+    # 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)
     ocserv = dict_merge(default_values, ocserv)
-    return ocserv
 
+    return ocserv
 
 def verify(ocserv):
     if ocserv is None:
         return None
 
     # Check authentication
     if "authentication" in ocserv:
         if "mode" in ocserv["authentication"]:
             if "local" in ocserv["authentication"]["mode"]:
                 if not ocserv["authentication"]["local_users"] or not ocserv["authentication"]["local_users"]["username"]:
                     raise ConfigError('openconnect mode local required at leat one user')
                 else:
                     for user in ocserv["authentication"]["local_users"]["username"]:
                         if not "password" in ocserv["authentication"]["local_users"]["username"][user]:
                             raise ConfigError(f'password required for user {user}')
         else:
             raise ConfigError('openconnect authentication mode required')
     else:
         raise ConfigError('openconnect authentication credentials required')
 
     # Check ssl
     if "ssl" in ocserv:
         req_cert = ['ca_cert_file', 'cert_file', 'key_file']
         for cert in req_cert:
             if not cert in ocserv["ssl"]:
                 raise ConfigError('openconnect ssl {0} required'.format(cert.replace('_', '-')))
     else:
         raise ConfigError('openconnect ssl required')
 
     # Check network settings
     if "network_settings" in ocserv:
         if "push_route" in ocserv["network_settings"]:
             # Replace default route
             if "0.0.0.0/0" in ocserv["network_settings"]["push_route"]:
                 ocserv["network_settings"]["push_route"].remove("0.0.0.0/0")
                 ocserv["network_settings"]["push_route"].append("default")
         else:
-            ocserv["network_settings"]["push_route"] = "default"  
+            ocserv["network_settings"]["push_route"] = "default"
     else:
         raise ConfigError('openconnect network settings required')
 
 
 def generate(ocserv):
     if not ocserv:
         return None
 
     if "radius" in ocserv["authentication"]["mode"]:
         # Render radius client configuration
-        render(radius_cfg, 'ocserv/radius_conf.tmpl', ocserv["authentication"]["radius"], trim_blocks=True)
+        render(radius_cfg, 'ocserv/radius_conf.tmpl', ocserv["authentication"]["radius"])
         # Render radius servers
-        render(radius_servers, 'ocserv/radius_servers.tmpl', ocserv["authentication"]["radius"], trim_blocks=True)
+        render(radius_servers, 'ocserv/radius_servers.tmpl', ocserv["authentication"]["radius"])
     else:
         if "local_users" in ocserv["authentication"]:
             for user in ocserv["authentication"]["local_users"]["username"]:
                 ocserv["authentication"]["local_users"]["username"][user]["hash"] = get_hash(ocserv["authentication"]["local_users"]["username"][user]["password"])
             # Render local users
-            render(ocserv_passwd, 'ocserv/ocserv_passwd.tmpl', ocserv["authentication"]["local_users"], trim_blocks=True)
+            render(ocserv_passwd, 'ocserv/ocserv_passwd.tmpl', ocserv["authentication"]["local_users"])
 
     # Render config
-    render(ocserv_conf, 'ocserv/ocserv_config.tmpl', ocserv, trim_blocks=True)
-    
+    render(ocserv_conf, 'ocserv/ocserv_config.tmpl', ocserv)
 
 
 def apply(ocserv):
     if not ocserv:
         call('systemctl stop ocserv.service')
         for file in [ocserv_conf, ocserv_passwd]:
             if os.path.exists(file):
                 os.unlink(file)
     else:
         call('systemctl restart ocserv.service')
 
 
 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/vpn_pptp.py b/src/conf_mode/vpn_pptp.py
index 3125ee9d0..30abe4782 100755
--- a/src/conf_mode/vpn_pptp.py
+++ b/src/conf_mode/vpn_pptp.py
@@ -1,296 +1,296 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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 re
 
 from copy import deepcopy
 from stat import S_IRUSR, S_IWUSR, S_IRGRP
 from sys import exit
 
 from vyos.config import Config
 from vyos.template import render
 from vyos.util import call, get_half_cpus
 from vyos import ConfigError
 
 from vyos import airbag
 airbag.enable()
 
 pptp_conf = '/run/accel-pppd/pptp.conf'
 pptp_chap_secrets = '/run/accel-pppd/pptp.chap-secrets'
 
 default_pptp = {
     'auth_mode' : 'local',
     'local_users' : [],
     'radius_server' : [],
     'radius_acct_inter_jitter': '',
     'radius_acct_tmo' : '30',
     'radius_max_try' : '3',
     'radius_timeout' : '30',
     'radius_nas_id' : '',
     'radius_nas_ip' : '',
     'radius_source_address' : '',
     'radius_shaper_attr' : '',
     'radius_shaper_vendor': '',
     'radius_dynamic_author' : '',
     'chap_secrets_file': pptp_chap_secrets, # used in Jinja2 template
     'outside_addr': '',
     'dnsv4': [],
     'wins': [],
     'client_ip_pool': '',
     'mtu': '1436',
     'auth_proto' : ['auth_mschap_v2'],
     'ppp_mppe' : 'prefer',
     'thread_cnt': get_half_cpus()
 }
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base_path = ['vpn', 'pptp', 'remote-access']
     if not conf.exists(base_path):
         return None
 
     pptp = deepcopy(default_pptp)
     conf.set_level(base_path)
 
     if conf.exists(['name-server']):
         pptp['dnsv4'] = conf.return_values(['name-server'])
 
     if conf.exists(['wins-server']):
         pptp['wins'] = conf.return_values(['wins-server'])
 
     if conf.exists(['outside-address']):
         pptp['outside_addr'] = conf.return_value(['outside-address'])
 
     if conf.exists(['authentication', 'mode']):
         pptp['auth_mode'] = conf.return_value(['authentication', 'mode'])
 
     #
     # local auth
     if conf.exists(['authentication', 'local-users']):
         for username in conf.list_nodes(['authentication', 'local-users', 'username']):
             user = {
                 'name': username,
                 'password' : '',
                 'state' : 'enabled',
                 'ip' : '*',
             }
 
             conf.set_level(base_path + ['authentication', 'local-users', 'username', username])
 
             if conf.exists(['password']):
                 user['password'] = conf.return_value(['password'])
 
             if conf.exists(['disable']):
                 user['state'] = 'disable'
 
             if conf.exists(['static-ip']):
                 user['ip'] = conf.return_value(['static-ip'])
 
             if not conf.exists(['disable']):
                 pptp['local_users'].append(user)
 
     #
     # RADIUS auth and settings
     conf.set_level(base_path + ['authentication', 'radius'])
     if conf.exists(['server']):
         for server in conf.list_nodes(['server']):
             radius = {
                 'server' : server,
                 'key' : '',
                 'fail_time' : 0,
                 'port' : '1812',
                 'acct_port' : '1813'
             }
 
             conf.set_level(base_path + ['authentication', 'radius', 'server', server])
 
             if conf.exists(['disable-accounting']):
                 radius['acct_port'] = '0'
 
             if conf.exists(['fail-time']):
                 radius['fail_time'] = conf.return_value(['fail-time'])
 
             if conf.exists(['port']):
                 radius['port'] = conf.return_value(['port'])
 
             if conf.exists(['acct-port']):
                 radius['acct_port'] = conf.return_value(['acct-port'])
 
             if conf.exists(['key']):
                 radius['key'] = conf.return_value(['key'])
 
             if not conf.exists(['disable']):
                 pptp['radius_server'].append(radius)
 
         #
         # advanced radius-setting
         conf.set_level(base_path + ['authentication', 'radius'])
 
         if conf.exists(['acct-interim-jitter']):
             pptp['radius_acct_inter_jitter'] = conf.return_value(['acct-interim-jitter'])
 
         if conf.exists(['acct-timeout']):
             pptp['radius_acct_tmo'] = conf.return_value(['acct-timeout'])
 
         if conf.exists(['max-try']):
             pptp['radius_max_try'] = conf.return_value(['max-try'])
 
         if conf.exists(['timeout']):
             pptp['radius_timeout'] = conf.return_value(['timeout'])
 
         if conf.exists(['nas-identifier']):
             pptp['radius_nas_id'] = conf.return_value(['nas-identifier'])
 
         if conf.exists(['nas-ip-address']):
             pptp['radius_nas_ip'] = conf.return_value(['nas-ip-address'])
 
         if conf.exists(['source-address']):
             pptp['radius_source_address'] = conf.return_value(['source-address'])
 
         # Dynamic Authorization Extensions (DOA)/Change Of Authentication (COA)
         if conf.exists(['dae-server']):
             dae = {
                 'port' : '',
                 'server' : '',
                 'key' : ''
             }
 
             if conf.exists(['dynamic-author', 'ip-address']):
                 dae['server'] = conf.return_value(['dynamic-author', 'ip-address'])
 
             if conf.exists(['dynamic-author', 'port']):
                 dae['port'] = conf.return_value(['dynamic-author', 'port'])
 
             if conf.exists(['dynamic-author', 'key']):
                 dae['key'] = conf.return_value(['dynamic-author', 'key'])
 
             pptp['radius_dynamic_author'] = dae
 
         if conf.exists(['rate-limit', 'enable']):
             pptp['radius_shaper_attr'] = 'Filter-Id'
             c_attr = ['rate-limit', 'enable', 'attribute']
             if conf.exists(c_attr):
                 pptp['radius_shaper_attr'] = conf.return_value(c_attr)
 
             c_vendor = ['rate-limit', 'enable', 'vendor']
             if conf.exists(c_vendor):
                 pptp['radius_shaper_vendor'] = conf.return_value(c_vendor)
 
     conf.set_level(base_path)
     if conf.exists(['client-ip-pool']):
         if conf.exists(['client-ip-pool', 'start']) and conf.exists(['client-ip-pool', 'stop']):
             start = conf.return_value(['client-ip-pool', 'start'])
             stop  = conf.return_value(['client-ip-pool', 'stop'])
             pptp['client_ip_pool'] = start + '-' + re.search('[0-9]+$', stop).group(0)
 
     if conf.exists(['mtu']):
         pptp['mtu'] = conf.return_value(['mtu'])
 
     # gateway address
     if conf.exists(['gateway-address']):
         pptp['gw_ip'] = conf.return_value(['gateway-address'])
     else:
         # calculate gw-ip-address
         if conf.exists(['client-ip-pool', 'start']):
             # use start ip as gw-ip-address
             pptp['gateway_address'] = conf.return_value(['client-ip-pool', 'start'])
 
     if conf.exists(['authentication', 'require']):
         # clear default list content, now populate with actual CLI values
         pptp['auth_proto'] = []
         auth_mods = {
             'pap': 'auth_pap',
             'chap': 'auth_chap_md5',
             'mschap': 'auth_mschap_v1',
             'mschap-v2': 'auth_mschap_v2'
         }
 
         for proto in conf.return_values(['authentication', 'require']):
             pptp['auth_proto'].append(auth_mods[proto])
 
     if conf.exists(['authentication', 'mppe']):
         pptp['ppp_mppe'] = conf.return_value(['authentication', 'mppe'])
 
     return pptp
 
 
 def verify(pptp):
     if not pptp:
         return None
 
     if pptp['auth_mode'] == 'local':
         if not pptp['local_users']:
             raise ConfigError('PPTP local auth mode requires local users to be configured!')
 
         for user in pptp['local_users']:
             username = user['name']
             if not user['password']:
                 raise ConfigError(f'Password required for local user "{username}"')
 
     elif pptp['auth_mode'] == 'radius':
         if len(pptp['radius_server']) == 0:
             raise ConfigError('RADIUS authentication requires at least one server')
 
         for radius in pptp['radius_server']:
             if not radius['key']:
                 server = radius['server']
                 raise ConfigError(f'Missing RADIUS secret key for server "{ server }"')
 
     if len(pptp['dnsv4']) > 2:
         raise ConfigError('Not more then two IPv4 DNS name-servers can be configured')
 
     if len(pptp['wins']) > 2:
         raise ConfigError('Not more then two IPv4 WINS name-servers can be configured')
 
 
 def generate(pptp):
     if not pptp:
         return None
 
-    render(pptp_conf, 'accel-ppp/pptp.config.tmpl', pptp, trim_blocks=True)
+    render(pptp_conf, 'accel-ppp/pptp.config.tmpl', pptp)
 
     if pptp['local_users']:
-        render(pptp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', pptp, trim_blocks=True)
+        render(pptp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', pptp)
         os.chmod(pptp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)
     else:
         if os.path.exists(pptp_chap_secrets):
              os.unlink(pptp_chap_secrets)
 
 
 def apply(pptp):
     if not pptp:
         call('systemctl stop accel-ppp@pptp.service')
         for file in [pptp_conf, pptp_chap_secrets]:
             if os.path.exists(file):
                 os.unlink(file)
 
         return None
 
     call('systemctl restart accel-ppp@pptp.service')
 
 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/vpn_sstp.py b/src/conf_mode/vpn_sstp.py
index 1b2b80ce5..47367f125 100755
--- a/src/conf_mode/vpn_sstp.py
+++ b/src/conf_mode/vpn_sstp.py
@@ -1,116 +1,116 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2018-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.config import Config
 from vyos.configdict import get_accel_dict
 from vyos.configverify import verify_accel_ppp_base_service
 from vyos.template import render
 from vyos.util import call
 from vyos.util import dict_search
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 sstp_conf = '/run/accel-pppd/sstp.conf'
 sstp_chap_secrets = '/run/accel-pppd/sstp.chap-secrets'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     base = ['vpn', 'sstp']
     if not conf.exists(base):
         return None
 
     # retrieve common dictionary keys
     sstp = get_accel_dict(conf, base, sstp_chap_secrets)
     return sstp
 
 def verify(sstp):
     if not sstp:
         return None
 
     verify_accel_ppp_base_service(sstp)
 
     if not sstp['client_ip_pool']:
         raise ConfigError('Client IP subnet required')
 
     #
     # SSL certificate checks
     #
     tmp = dict_search('ssl.ca_cert_file', sstp)
     if not tmp:
         raise ConfigError(f'SSL CA certificate file required!')
     else:
         if not os.path.isfile(tmp):
             raise ConfigError(f'SSL CA certificate "{tmp}" does not exist!')
 
     tmp = dict_search('ssl.cert_file', sstp)
     if not tmp:
         raise ConfigError(f'SSL public key file required!')
     else:
         if not os.path.isfile(tmp):
             raise ConfigError(f'SSL public key "{tmp}" does not exist!')
 
     tmp = dict_search('ssl.key_file', sstp)
     if not tmp:
         raise ConfigError(f'SSL private key file required!')
     else:
         if not os.path.isfile(tmp):
             raise ConfigError(f'SSL private key "{tmp}" does not exist!')
 
 def generate(sstp):
     if not sstp:
         return None
 
     # accel-cmd reload doesn't work so any change results in a restart of the daemon
-    render(sstp_conf, 'accel-ppp/sstp.config.tmpl', sstp, trim_blocks=True)
+    render(sstp_conf, 'accel-ppp/sstp.config.tmpl', sstp)
 
     if dict_search('authentication.mode', sstp) == 'local':
         render(sstp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl',
-               sstp, trim_blocks=True, permission=0o640)
+               sstp, permission=0o640)
     else:
         if os.path.exists(sstp_chap_secrets):
              os.unlink(sstp_chap_secrets)
 
     return sstp
 
 def apply(sstp):
     if not sstp:
         call('systemctl stop accel-ppp@sstp.service')
         for file in [sstp_chap_secrets, sstp_conf]:
             if os.path.exists(file):
                 os.unlink(file)
 
         return None
 
     call('systemctl restart accel-ppp@sstp.service')
 
 
 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/op_mode/lldp_op.py b/src/op_mode/lldp_op.py
index 172ce71b7..fa19e7d45 100755
--- a/src/op_mode/lldp_op.py
+++ b/src/op_mode/lldp_op.py
@@ -1,124 +1,124 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019 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 argparse
 import jinja2
 import json
 
 from sys import exit
 from tabulate import tabulate
 
 from vyos.util import cmd
 from vyos.config import Config
 
 parser = argparse.ArgumentParser()
 parser.add_argument("-a", "--all", action="store_true", help="Show LLDP neighbors on all interfaces")
 parser.add_argument("-d", "--detail", action="store_true", help="Show detailes LLDP neighbor information on all interfaces")
 parser.add_argument("-i", "--interface", action="store", help="Show LLDP neighbors on specific interface")
 
 # Please be careful if you edit the template.
 lldp_out = """Capability Codes: R - Router, B - Bridge, W - Wlan r - Repeater, S - Station
                   D - Docsis, T - Telephone, O - Other
 
 Device ID                 Local     Proto  Cap   Platform             Port ID
 ---------                 -----     -----  ---   --------             -------
 {% for neighbor in neighbors %}
 {%   for local_if, info in neighbor.items() %}
 {{ "%-25s" | format(info.chassis) }} {{ "%-9s" | format(local_if) }} {{ "%-6s" | format(info.proto) }} {{ "%-5s" | format(info.capabilities) }} {{ "%-20s" | format(info.platform[:18]) }} {{ info.remote_if }}
 {%   endfor %}
 {% endfor %}
 """
 
 def get_neighbors():
     return cmd('/usr/sbin/lldpcli -f json show neighbors')
 
 def parse_data(data):
     output = []
     for local_if, values in data.items():
         for chassis, c_value in values.get('chassis', {}).items():
             capabilities = c_value['capability']
             if isinstance(capabilities, dict):
                 capabilities = [capabilities]
 
             cap = ''
             for capability in capabilities:
                 if capability['enabled']:
                     if capability['type'] == 'Router':
                         cap += 'R'
                     if capability['type'] == 'Bridge':
                         cap += 'B'
                     if capability['type'] == 'Wlan':
                         cap += 'W'
                     if capability['type'] == 'Station':
                         cap += 'S'
                     if capability['type'] == 'Repeater':
                         cap += 'r'
                     if capability['type'] == 'Telephone':
                         cap += 'T'
                     if capability['type'] == 'Docsis':
                         cap += 'D'
                     if capability['type'] == 'Other':
                         cap += 'O'
 
 
         remote_if = 'Unknown'
         if 'descr' in values.get('port', {}):
             remote_if = values.get('port', {}).get('descr')
         elif 'id' in values.get('port', {}):
             remote_if = values.get('port', {}).get('id').get('value', 'Unknown')
 
         output.append({local_if: {'chassis': chassis,
                                    'remote_if': remote_if,
                                    'proto': values.get('via','Unknown'),
                                    'platform': c_value.get('descr', 'Unknown'),
                                    'capabilities': cap}})
 
 
     output = {'neighbors': output}
     return output
 
 if __name__ == '__main__':
     args = parser.parse_args()
     tmp = { 'neighbors' : [] }
 
     c = Config()
     if not c.exists_effective(['service', 'lldp']):
         print('Service LLDP is not configured')
         exit(0)
 
     if args.detail:
         print(cmd('/usr/sbin/lldpctl -f plain'))
         exit(0)
     elif args.all or args.interface:
         tmp = json.loads(get_neighbors())
         neighbors = dict()
 
         if 'interface' in tmp.get('lldp'):
             if args.all:
                 neighbors = tmp['lldp']['interface']
             elif args.interface:
                 if args.interface in tmp['lldp']['interface']:
                     neighbors[args.interface] = tmp['lldp']['interface'][args.interface]
 
     else:
         parser.print_help()
         exit(1)
 
-    tmpl = jinja2.Template(lldp_out, trim_blocks=True)
+    tmpl = jinja2.Template(lldp_out)
     config_text = tmpl.render(parse_data(neighbors))
     print(config_text)
 
     exit(0)