diff --git a/src/conf_mode/interfaces_bonding.py b/src/conf_mode/interfaces_bonding.py
index 5f839b33c..0844d2913 100755
--- a/src/conf_mode/interfaces_bonding.py
+++ b/src/conf_mode/interfaces_bonding.py
@@ -1,297 +1,299 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-2024 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.configdict import get_frrender_dict
 from vyos.configdict import get_interface_dict
 from vyos.configdict import is_node_changed
 from vyos.configdict import leaf_node_changed
 from vyos.configdict import is_member
 from vyos.configdict import is_source_interface
 from vyos.configverify import verify_address
 from vyos.configverify import verify_bridge_delete
 from vyos.configverify import verify_dhcpv6
 from vyos.configverify import verify_eapol
 from vyos.configverify import verify_mirror_redirect
 from vyos.configverify import verify_mtu_ipv6
 from vyos.configverify import verify_vlan_config
 from vyos.configverify import verify_vrf
 from vyos.frrender import FRRender
 from vyos.ifconfig import BondIf
 from vyos.ifconfig.ethernet import EthernetIf
 from vyos.ifconfig import Section
 from vyos.utils.assertion import assert_mac
 from vyos.utils.dict import dict_search
 from vyos.utils.dict import dict_to_paths_values
 from vyos.utils.network import interface_exists
+from vyos.utils.process import is_systemd_service_running
 from vyos.configdict import has_address_configured
 from vyos.configdict import has_vrf_configured
-from vyos.configdep import set_dependents, call_dependents
+from vyos.configdep import set_dependents
+from vyos.configdep import call_dependents
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_bond_mode(mode):
     if mode == 'round-robin':
         return 'balance-rr'
     elif mode == 'active-backup':
         return 'active-backup'
     elif mode == 'xor-hash':
         return 'balance-xor'
     elif mode == 'broadcast':
         return 'broadcast'
     elif mode == '802.3ad':
         return '802.3ad'
     elif mode == 'transmit-load-balance':
         return 'balance-tlb'
     elif mode == 'adaptive-load-balance':
         return 'balance-alb'
     else:
         raise ConfigError(f'invalid bond mode "{mode}"')
 
 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', 'bonding']
     ifname, bond = get_interface_dict(conf, base, with_pki=True)
 
     # To make our own life easier transfor the list of member interfaces
     # into a dictionary - we will use this to add additional information
     # later on for each member
     if 'member' in bond and 'interface' in bond['member']:
         # convert list of member interfaces to a dictionary
         bond['member']['interface'] = {k: {} for k in bond['member']['interface']}
 
     if 'mode' in bond:
         bond['mode'] = get_bond_mode(bond['mode'])
 
     tmp = is_node_changed(conf, base + [ifname, 'mode'])
     if tmp: bond.update({'shutdown_required' : {}})
 
     tmp = is_node_changed(conf, base + [ifname, 'lacp-rate'])
     if tmp: bond.update({'shutdown_required' : {}})
 
     tmp = is_node_changed(conf, base + [ifname, 'evpn'])
     if tmp: bond.update({'frr_dict' : get_frrender_dict(conf)})
 
     # determine which members have been removed
     interfaces_removed = leaf_node_changed(conf, base + [ifname, 'member', 'interface'])
     # Reset config level to interfaces
     old_level = conf.get_level()
     conf.set_level(['interfaces'])
 
     if interfaces_removed:
         bond['shutdown_required'] = {}
         if 'member' not in bond:
             bond['member'] = {}
 
         tmp = {}
         for interface in interfaces_removed:
             # if member is deleted from bond, add dependencies to call
             # ethernet commit again in apply function
             # to apply options under ethernet section
             set_dependents('ethernet', conf, interface)
             section = Section.section(interface) # this will be 'ethernet' for 'eth0'
             if conf.exists([section, interface, 'disable']):
                 tmp[interface] = {'disable': ''}
             else:
                 tmp[interface] = {}
 
         # also present the interfaces to be removed from the bond as dictionary
         bond['member']['interface_remove'] = tmp
 
     # Restore existing config level
     conf.set_level(old_level)
 
     if dict_search('member.interface', bond):
         for interface, interface_config in bond['member']['interface'].items():
 
             interface_ethernet_config = conf.get_config_dict(
                 ['interfaces', 'ethernet', interface],
                 key_mangling=('-', '_'),
                 get_first_key=True,
                 no_tag_node_value_mangle=True,
                 with_defaults=False,
                 with_recursive_defaults=False)
 
             interface_config['config_paths'] = dict_to_paths_values(interface_ethernet_config)
 
             # Check if member interface is a new member
             if not conf.exists_effective(base + [ifname, 'member', 'interface', interface]):
                 bond['shutdown_required'] = {}
                 interface_config['new_added'] = {}
 
             # Check if member interface is disabled
             conf.set_level(['interfaces'])
 
             section = Section.section(interface) # this will be 'ethernet' for 'eth0'
             if conf.exists([section, interface, 'disable']):
                 interface_config['disable'] = ''
 
             conf.set_level(old_level)
 
             # Check if member interface is already member of another bridge
             tmp = is_member(conf, interface, 'bridge')
             if tmp: interface_config['is_bridge_member'] = tmp
 
             # Check if member interface is already member of a bond
             tmp = is_member(conf, interface, 'bonding')
             for tmp in is_member(conf, interface, 'bonding'):
                 if bond['ifname'] == tmp:
                     continue
                 interface_config['is_bond_member'] = tmp
 
             # Check if member interface is used as source-interface on another interface
             tmp = is_source_interface(conf, interface)
             if tmp: interface_config['is_source_interface'] = tmp
 
             # bond members must not have an assigned address
             tmp = has_address_configured(conf, interface)
             if tmp: interface_config['has_address'] = {}
 
             # bond members must not have a VRF attached
             tmp = has_vrf_configured(conf, interface)
             if tmp: interface_config['has_vrf'] = {}
     return bond
 
 
 def verify(bond):
     if 'deleted' in bond:
         verify_bridge_delete(bond)
         return None
 
     if 'arp_monitor' in bond:
         if 'target' in bond['arp_monitor'] and len(bond['arp_monitor']['target']) > 16:
             raise ConfigError('The maximum number of arp-monitor targets is 16')
 
         if 'interval' in bond['arp_monitor'] and int(bond['arp_monitor']['interval']) > 0:
             if bond['mode'] in ['802.3ad', 'balance-tlb', 'balance-alb']:
                 raise ConfigError('ARP link monitoring does not work for mode 802.3ad, ' \
                                   'transmit-load-balance or adaptive-load-balance')
 
     if 'primary' in bond:
         if bond['mode'] not in ['active-backup', 'balance-tlb', 'balance-alb']:
             raise ConfigError('Option primary - mode dependency failed, not'
                               'supported in mode {mode}!'.format(**bond))
 
     verify_mtu_ipv6(bond)
     verify_address(bond)
     verify_dhcpv6(bond)
     verify_vrf(bond)
     verify_mirror_redirect(bond)
     verify_eapol(bond)
 
     # use common function to verify VLAN configuration
     verify_vlan_config(bond)
 
     bond_name = bond['ifname']
     if dict_search('member.interface', bond):
         for interface, interface_config in bond['member']['interface'].items():
             error_msg = f'Can not add interface "{interface}" to bond, '
 
             if interface == 'lo':
                 raise ConfigError('Loopback interface "lo" can not be added to a bond')
 
             if not interface_exists(interface):
                 raise ConfigError(error_msg + 'it does not exist!')
 
             if 'is_bridge_member' in interface_config:
                 tmp = next(iter(interface_config['is_bridge_member']))
                 raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!')
 
             if 'is_bond_member' in interface_config:
                 tmp = next(iter(interface_config['is_bond_member']))
                 raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!')
 
             if 'is_source_interface' in interface_config:
                 tmp = interface_config['is_source_interface']
                 raise ConfigError(error_msg + f'it is the source-interface of "{tmp}"!')
 
             if 'has_address' in interface_config:
                 raise ConfigError(error_msg + 'it has an address assigned!')
 
             if 'has_vrf' in interface_config:
                 raise ConfigError(error_msg + 'it has a VRF assigned!')
 
             if 'new_added' in interface_config and 'config_paths' in interface_config:
                 for option_path, option_value in interface_config['config_paths'].items():
                     if option_path in EthernetIf.get_bond_member_allowed_options() :
                         continue
                     if option_path in BondIf.get_inherit_bond_options():
                         continue
                     raise ConfigError(error_msg + f'it has a "{option_path.replace(".", " ")}" assigned!')
 
     if 'primary' in bond:
         if bond['primary'] not in bond['member']['interface']:
             raise ConfigError(f'Primary interface of bond "{bond_name}" must be a member interface')
 
         if bond['mode'] not in ['active-backup', 'balance-tlb', 'balance-alb']:
             raise ConfigError('primary interface only works for mode active-backup, ' \
                               'transmit-load-balance or adaptive-load-balance')
 
     if 'system_mac' in bond:
         if bond['mode'] != '802.3ad':
             raise ConfigError('Actor MAC address only available in 802.3ad mode!')
 
         system_mac = bond['system_mac']
         try:
             assert_mac(system_mac, test_all_zero=False)
         except:
             raise ConfigError(f'Cannot use a multicast MAC address "{system_mac}" as system-mac!')
 
     return None
 
 def generate(bond):
-    if 'frr_dict' in bond and 'frrender_cls' not in bond['frr_dict']:
+    if 'frr_dict' in bond and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(bond['frr_dict'])
     return None
 
 def apply(bond):
-    if 'frr_dict' in bond and 'frrender_cls' not in bond['frr_dict']:
+    if 'frr_dict' in bond and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
 
     b = BondIf(bond['ifname'])
     if 'deleted' in bond:
         b.remove()
     else:
         b.update(bond)
 
     if dict_search('member.interface_remove', bond):
         try:
             call_dependents()
         except ConfigError:
             raise ConfigError('Error in updating ethernet interface '
                               'after deleting it from bond')
 
     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_ethernet.py b/src/conf_mode/interfaces_ethernet.py
index accfb6b8e..5024e6982 100755
--- a/src/conf_mode/interfaces_ethernet.py
+++ b/src/conf_mode/interfaces_ethernet.py
@@ -1,348 +1,347 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-2024 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.base import Warning
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configdict import get_interface_dict
 from vyos.configdict import is_node_changed
 from vyos.configverify import verify_address
 from vyos.configverify import verify_dhcpv6
 from vyos.configverify import verify_interface_exists
 from vyos.configverify import verify_mirror_redirect
 from vyos.configverify import verify_mtu
 from vyos.configverify import verify_mtu_ipv6
 from vyos.configverify import verify_vlan_config
 from vyos.configverify import verify_vrf
 from vyos.configverify import verify_bond_bridge_member
 from vyos.configverify import verify_eapol
 from vyos.ethtool import Ethtool
 from vyos.frrender import FRRender
 from vyos.ifconfig import EthernetIf
 from vyos.ifconfig import BondIf
 from vyos.utils.dict import dict_search
 from vyos.utils.dict import dict_to_paths_values
 from vyos.utils.dict import dict_set
 from vyos.utils.dict import dict_delete
+from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def update_bond_options(conf: Config, eth_conf: dict) -> list:
     """
     Return list of blocked options if interface is a bond member
     :param conf: Config object
     :type conf: Config
     :param eth_conf: Ethernet config dictionary
     :type eth_conf: dict
     :return: List of blocked options
     :rtype: list
     """
     blocked_list = []
     bond_name = list(eth_conf['is_bond_member'].keys())[0]
     config_without_defaults = conf.get_config_dict(
         ['interfaces', 'ethernet', eth_conf['ifname']],
         key_mangling=('-', '_'),
         get_first_key=True,
         no_tag_node_value_mangle=True,
         with_defaults=False,
         with_recursive_defaults=False)
     config_with_defaults = conf.get_config_dict(
         ['interfaces', 'ethernet', eth_conf['ifname']],
         key_mangling=('-', '_'),
         get_first_key=True,
         no_tag_node_value_mangle=True,
         with_defaults=True,
         with_recursive_defaults=True)
     bond_config_with_defaults = conf.get_config_dict(
         ['interfaces', 'bonding', bond_name],
         key_mangling=('-', '_'),
         get_first_key=True,
         no_tag_node_value_mangle=True,
         with_defaults=True,
         with_recursive_defaults=True)
     eth_dict_paths = dict_to_paths_values(config_without_defaults)
     eth_path_base = ['interfaces', 'ethernet', eth_conf['ifname']]
 
     #if option is configured under ethernet section
     for option_path, option_value in eth_dict_paths.items():
         bond_option_value = dict_search(option_path, bond_config_with_defaults)
 
         #If option is allowed for changing then continue
         if option_path in EthernetIf.get_bond_member_allowed_options():
             continue
         # if option is inherited from bond then set valued from bond interface
         if option_path in BondIf.get_inherit_bond_options():
             # If option equals to bond option then do nothing
             if option_value == bond_option_value:
                 continue
             else:
                 # if ethernet has option and bond interface has
                 # then copy it from bond
                 if bond_option_value is not None:
                     if is_node_changed(conf, eth_path_base + option_path.split('.')):
                         Warning(
                             f'Cannot apply "{option_path.replace(".", " ")}" to "{option_value}".' \
                             f' Interface "{eth_conf["ifname"]}" is a bond member.' \
                             f' Option is inherited from bond "{bond_name}"')
                     dict_set(option_path, bond_option_value, eth_conf)
                     continue
                 # if ethernet has option and bond interface does not have
                 # then delete it form dict and do not apply it
                 else:
                     if is_node_changed(conf, eth_path_base + option_path.split('.')):
                         Warning(
                             f'Cannot apply "{option_path.replace(".", " ")}".' \
                             f' Interface "{eth_conf["ifname"]}" is a bond member.' \
                             f' Option is inherited from bond "{bond_name}"')
                     dict_delete(option_path, eth_conf)
         blocked_list.append(option_path)
 
     # if inherited option is not configured under ethernet section but configured under bond section
     for option_path in BondIf.get_inherit_bond_options():
         bond_option_value = dict_search(option_path, bond_config_with_defaults)
         if bond_option_value is not None:
             if option_path not in eth_dict_paths:
                 if is_node_changed(conf, eth_path_base + option_path.split('.')):
                     Warning(
                         f'Cannot apply "{option_path.replace(".", " ")}" to "{dict_search(option_path, config_with_defaults)}".' \
                         f' Interface "{eth_conf["ifname"]}" is a bond member. ' \
                         f'Option is inherited from bond "{bond_name}"')
                 dict_set(option_path, bond_option_value, eth_conf)
     eth_conf['bond_blocked_changes'] = blocked_list
     return None
 
 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', 'ethernet']
     ifname, ethernet = get_interface_dict(conf, base, with_pki=True)
 
     # T5862 - default MTU is not acceptable in some environments
     # There are cloud environments available where the maximum supported
     # ethernet MTU is e.g. 1450 bytes, thus we clamp this to the adapters
     # maximum MTU value or 1500 bytes - whatever is lower
     if 'mtu' not in ethernet:
         try:
             ethernet['mtu'] = '1500'
             max_mtu = EthernetIf(ifname).get_max_mtu()
             if max_mtu < int(ethernet['mtu']):
                 ethernet['mtu'] = str(max_mtu)
         except:
             pass
 
     if 'is_bond_member' in ethernet:
         update_bond_options(conf, ethernet)
 
     tmp = is_node_changed(conf, base + [ifname, 'speed'])
     if tmp: ethernet.update({'speed_duplex_changed': {}})
 
     tmp = is_node_changed(conf, base + [ifname, 'duplex'])
     if tmp: ethernet.update({'speed_duplex_changed': {}})
 
     tmp = is_node_changed(conf, base + [ifname, 'evpn'])
     if tmp: ethernet.update({'frr_dict' : get_frrender_dict(conf)})
 
     return ethernet
 
 def verify_speed_duplex(ethernet: dict, ethtool: Ethtool):
     """
      Verify speed and duplex
     :param ethernet: dictionary which is received from get_interface_dict
     :type ethernet: dict
     :param ethtool: Ethernet object
     :type ethtool: Ethtool
     """
     if ((ethernet['speed'] == 'auto' and ethernet['duplex'] != 'auto') or
             (ethernet['speed'] != 'auto' and ethernet['duplex'] == 'auto')):
         raise ConfigError(
             'Speed/Duplex missmatch. Must be both auto or manually configured')
 
     if ethernet['speed'] != 'auto' and ethernet['duplex'] != 'auto':
         # We need to verify if the requested speed and duplex setting is
         # supported by the underlaying NIC.
         speed = ethernet['speed']
         duplex = ethernet['duplex']
         if not ethtool.check_speed_duplex(speed, duplex):
             raise ConfigError(
                 f'Adapter does not support changing speed ' \
                 f'and duplex settings to: {speed}/{duplex}!')
 
 
 def verify_flow_control(ethernet: dict, ethtool: Ethtool):
     """
      Verify flow control
     :param ethernet: dictionary which is received from get_interface_dict
     :type ethernet: dict
     :param ethtool: Ethernet object
     :type ethtool: Ethtool
     """
     if 'disable_flow_control' in ethernet:
         if not ethtool.check_flow_control():
             raise ConfigError(
                 'Adapter does not support changing flow-control settings!')
 
 
 def verify_ring_buffer(ethernet: dict, ethtool: Ethtool):
     """
      Verify ring buffer
     :param ethernet: dictionary which is received from get_interface_dict
     :type ethernet: dict
     :param ethtool: Ethernet object
     :type ethtool: Ethtool
     """
     if 'ring_buffer' in ethernet:
         max_rx = ethtool.get_ring_buffer_max('rx')
         if not max_rx:
             raise ConfigError(
                 'Driver does not support RX ring-buffer configuration!')
 
         max_tx = ethtool.get_ring_buffer_max('tx')
         if not max_tx:
             raise ConfigError(
                 'Driver does not support TX ring-buffer configuration!')
 
         rx = dict_search('ring_buffer.rx', ethernet)
         if rx and int(rx) > int(max_rx):
             raise ConfigError(f'Driver only supports a maximum RX ring-buffer ' \
                               f'size of "{max_rx}" bytes!')
 
         tx = dict_search('ring_buffer.tx', ethernet)
         if tx and int(tx) > int(max_tx):
             raise ConfigError(f'Driver only supports a maximum TX ring-buffer ' \
                               f'size of "{max_tx}" bytes!')
 
 
 def verify_offload(ethernet: dict, ethtool: Ethtool):
     """
      Verify offloading capabilities
     :param ethernet: dictionary which is received from get_interface_dict
     :type ethernet: dict
     :param ethtool: Ethernet object
     :type ethtool: Ethtool
     """
     if dict_search('offload.rps', ethernet) != None:
         if not os.path.exists(f'/sys/class/net/{ethernet["ifname"]}/queues/rx-0/rps_cpus'):
             raise ConfigError('Interface does not suport RPS!')
     driver = ethtool.get_driver_name()
     # T3342 - Xen driver requires special treatment
     if driver == 'vif':
         if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None:
             raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\
                               'for MTU size larger then 1500 bytes')
 
 
 def verify_allowedbond_changes(ethernet: dict):
     """
      Verify changed options if interface is in bonding
     :param ethernet: dictionary which is received from get_interface_dict
     :type ethernet: dict
     """
     if 'bond_blocked_changes' in ethernet:
         for option in ethernet['bond_blocked_changes']:
             raise ConfigError(f'Cannot configure "{option.replace(".", " ")}"' \
                               f' on interface "{ethernet["ifname"]}".' \
                               f' Interface is a bond member')
 
 def verify(ethernet):
     if 'deleted' in ethernet:
         return None
     if 'is_bond_member' in ethernet:
         verify_bond_member(ethernet)
     else:
         verify_ethernet(ethernet)
 
 
 def verify_bond_member(ethernet):
     """
      Verification function for ethernet interface which is in bonding
     :param ethernet: dictionary which is received from get_interface_dict
     :type ethernet: dict
     """
     ifname = ethernet['ifname']
     verify_interface_exists(ethernet, ifname)
     verify_eapol(ethernet)
     verify_mirror_redirect(ethernet)
     ethtool = Ethtool(ifname)
     verify_speed_duplex(ethernet, ethtool)
     verify_flow_control(ethernet, ethtool)
     verify_ring_buffer(ethernet, ethtool)
     verify_offload(ethernet, ethtool)
     verify_allowedbond_changes(ethernet)
 
 def verify_ethernet(ethernet):
     """
      Verification function for simple ethernet interface
     :param ethernet: dictionary which is received from get_interface_dict
     :type ethernet: dict
     """
     ifname = ethernet['ifname']
     verify_interface_exists(ethernet, ifname)
     verify_mtu(ethernet)
     verify_mtu_ipv6(ethernet)
     verify_dhcpv6(ethernet)
     verify_address(ethernet)
     verify_vrf(ethernet)
     verify_bond_bridge_member(ethernet)
     verify_eapol(ethernet)
     verify_mirror_redirect(ethernet)
     ethtool = Ethtool(ifname)
     # No need to check speed and duplex keys as both have default values.
     verify_speed_duplex(ethernet, ethtool)
     verify_flow_control(ethernet, ethtool)
     verify_ring_buffer(ethernet, ethtool)
     verify_offload(ethernet, ethtool)
     # use common function to verify VLAN configuration
     verify_vlan_config(ethernet)
     return None
 
 def generate(ethernet):
-    if 'frr_dict' in ethernet and 'frrender_cls' not in ethernet['frr_dict']:
+    if 'frr_dict' in ethernet and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(ethernet['frr_dict'])
     return None
 
 def apply(ethernet):
-    if 'frr_dict' in ethernet and 'frrender_cls' not in ethernet['frr_dict']:
+    if 'frr_dict' in ethernet and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
-
     e = EthernetIf(ethernet['ifname'])
     if 'deleted' in ethernet:
         e.remove()
     else:
         e.update(ethernet)
     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/policy.py b/src/conf_mode/policy.py
index 4abb150ac..5e71a612d 100755
--- a/src/conf_mode/policy.py
+++ b/src/conf_mode/policy.py
@@ -1,282 +1,283 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2021-2024 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.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.frrender import FRRender
 from vyos.frrender import frr_protocols
 from vyos.utils.dict import dict_search
+from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def community_action_compatibility(actions: dict) -> bool:
     """
     Check compatibility of values in community and large community sections
     :param actions: dictionary with community
     :type actions: dict
     :return: true if compatible, false if not
     :rtype: bool
     """
     if ('none' in actions) and ('replace' in actions or 'add' in actions):
         return False
     if 'replace' in actions and 'add' in actions:
         return False
     if ('delete' in actions) and ('none' in actions or 'replace' in actions):
         return False
     return True
 
 
 def extcommunity_action_compatibility(actions: dict) -> bool:
     """
     Check compatibility of values in extended community sections
     :param actions: dictionary with community
     :type actions: dict
     :return: true if compatible, false if not
     :rtype: bool
     """
     if ('none' in actions) and (
             'rt' in actions or 'soo' in actions or 'bandwidth' in actions or 'bandwidth_non_transitive' in actions):
         return False
     if ('bandwidth_non_transitive' in actions) and ('bandwidth' not in actions):
         return False
     return True
 
 def routing_policy_find(key, dictionary):
     # Recursively traverse a dictionary and extract the value assigned to
     # a given key as generator object. This is made for routing policies,
     # thus also import/export is checked
     for k, v in dictionary.items():
         if k == key:
             if isinstance(v, dict):
                 for a, b in v.items():
                     if a in ['import', 'export']:
                         yield b
             else:
                 yield v
         elif isinstance(v, dict):
             for result in routing_policy_find(key, v):
                 yield result
         elif isinstance(v, list):
             for d in v:
                 if isinstance(d, dict):
                     for result in routing_policy_find(key, d):
                         yield result
 
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf)
 
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'policy'):
         return None
 
     policy_types = ['access_list', 'access_list6', 'as_path_list',
                     'community_list', 'extcommunity_list',
                     'large_community_list', 'prefix_list',
                     'prefix_list6', 'route_map']
 
     policy = config_dict['policy']
     for protocol in frr_protocols:
         if protocol not in config_dict:
             continue
         if 'protocol' not in policy:
             policy.update({'protocol': {}})
         policy['protocol'].update({protocol : config_dict[protocol]})
 
     for policy_type in policy_types:
         # Bail out early and continue with next policy type
         if policy_type not in policy:
             continue
 
         # instance can be an ACL name/number, prefix-list name or route-map name
         for instance, instance_config in policy[policy_type].items():
             # If no rule was found within the instance ... sad, but we can leave
             # early as nothing needs to be verified
             if 'rule' not in instance_config:
                 continue
 
             # human readable instance name (hypen instead of underscore)
             policy_hr = policy_type.replace('_', '-')
             entries = []
             for rule, rule_config in instance_config['rule'].items():
                 mandatory_error = f'must be specified for "{policy_hr} {instance} rule {rule}"!'
                 if 'action' not in rule_config:
                     raise ConfigError(f'Action {mandatory_error}')
 
                 if policy_type == 'access_list':
                     if 'source' not in rule_config:
                         raise ConfigError(f'A source {mandatory_error}')
 
                     if int(instance) in range(100, 200) or int(
                             instance) in range(2000, 2700):
                         if 'destination' not in rule_config:
                             raise ConfigError(
                                 f'A destination {mandatory_error}')
 
                 if policy_type == 'access_list6':
                     if 'source' not in rule_config:
                         raise ConfigError(f'A source {mandatory_error}')
 
                 if policy_type in ['as_path_list', 'community_list',
                                    'extcommunity_list',
                                    'large_community_list']:
                     if 'regex' not in rule_config:
                         raise ConfigError(f'A regex {mandatory_error}')
 
                 if policy_type in ['prefix_list', 'prefix_list6']:
                     if 'prefix' not in rule_config:
                         raise ConfigError(f'A prefix {mandatory_error}')
 
                     if rule_config in entries:
                         raise ConfigError(
                             f'Rule "{rule}" contains a duplicate prefix definition!')
                     entries.append(rule_config)
 
     # route-maps tend to be a bit more complex so they get their own verify() section
     if 'route_map' in policy:
         for route_map, route_map_config in policy['route_map'].items():
             if 'rule' not in route_map_config:
                 continue
 
             for rule, rule_config in route_map_config['rule'].items():
                 # Action 'deny' cannot be used with "continue" or "on-match"
                 # FRR does not validate it T4827, T6676
                 if rule_config['action'] == 'deny' and ('continue' in rule_config or 'on_match' in rule_config):
                     raise ConfigError(f'rule {rule} "continue" or "on-match" cannot be used with action deny!')
 
                 # Specified community-list must exist
                 tmp = dict_search('match.community.community_list',
                                   rule_config)
                 if tmp and tmp not in policy.get('community_list', []):
                     raise ConfigError(f'community-list {tmp} does not exist!')
 
                 # Specified extended community-list must exist
                 tmp = dict_search('match.extcommunity', rule_config)
                 if tmp and tmp not in policy.get('extcommunity_list', []):
                     raise ConfigError(
                         f'extcommunity-list {tmp} does not exist!')
 
                 # Specified large-community-list must exist
                 tmp = dict_search('match.large_community.large_community_list',
                                   rule_config)
                 if tmp and tmp not in policy.get('large_community_list', []):
                     raise ConfigError(
                         f'large-community-list {tmp} does not exist!')
 
                 # Specified prefix-list must exist
                 tmp = dict_search('match.ip.address.prefix_list', rule_config)
                 if tmp and tmp not in policy.get('prefix_list', []):
                     raise ConfigError(f'prefix-list {tmp} does not exist!')
 
                 # Specified prefix-list must exist
                 tmp = dict_search('match.ipv6.address.prefix_list',
                                   rule_config)
                 if tmp and tmp not in policy.get('prefix_list6', []):
                     raise ConfigError(f'prefix-list6 {tmp} does not exist!')
 
                 # Specified access_list6 in nexthop must exist
                 tmp = dict_search('match.ipv6.nexthop.access_list',
                                   rule_config)
                 if tmp and tmp not in policy.get('access_list6', []):
                     raise ConfigError(f'access_list6 {tmp} does not exist!')
 
                 # Specified prefix-list6 in nexthop must exist
                 tmp = dict_search('match.ipv6.nexthop.prefix_list',
                                   rule_config)
                 if tmp and tmp not in policy.get('prefix_list6', []):
                     raise ConfigError(f'prefix-list6 {tmp} does not exist!')
 
                 tmp = dict_search('set.community.delete', rule_config)
                 if tmp and tmp not in policy.get('community_list', []):
                     raise ConfigError(f'community-list {tmp} does not exist!')
 
                 tmp = dict_search('set.large_community.delete',
                                   rule_config)
                 if tmp and tmp not in policy.get('large_community_list', []):
                     raise ConfigError(
                         f'large-community-list {tmp} does not exist!')
 
                 if 'set' in rule_config:
                     rule_action = rule_config['set']
                     if 'community' in rule_action:
                         if not community_action_compatibility(
                                 rule_action['community']):
                             raise ConfigError(
                                 f'Unexpected combination between action replace, add, delete or none in community')
                     if 'large_community' in rule_action:
                         if not community_action_compatibility(
                                 rule_action['large_community']):
                             raise ConfigError(
                                 f'Unexpected combination between action replace, add, delete or none in large-community')
                     if 'extcommunity' in rule_action:
                         if not extcommunity_action_compatibility(
                                 rule_action['extcommunity']):
                             raise ConfigError(
                                 f'Unexpected combination between none, rt, soo, bandwidth, bandwidth-non-transitive in extended-community')
     # When routing protocols are active some use prefix-lists, route-maps etc.
     # to apply the systems routing policy to the learned or redistributed routes.
     # When the "routing policy" changes and policies, route-maps etc. are deleted,
     # it is our responsibility to verify that the policy can not be deleted if it
     # is used by any routing protocol
     # Check if any routing protocol is activated
     if 'protocol' in policy:
         for policy_type in policy_types:
             for policy_name in list(set(routing_policy_find(policy_type, policy['protocol']))):
                 found = False
                 if policy_type in policy and policy_name in policy[policy_type]:
                     found = True
                 # BGP uses prefix-list for selecting both an IPv4 or IPv6 AFI related
                 # list - we need to go the extra mile here and check both prefix-lists
                 if policy_type == 'prefix_list' and 'prefix_list6' in policy and policy_name in \
                         policy['prefix_list6']:
                     found = True
                 if not found:
                     tmp = policy_type.replace('_', '-')
                     raise ConfigError(
                         f'Can not delete {tmp} "{policy_name}", still in use!')
 
     return None
 
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_babel.py b/src/conf_mode/protocols_babel.py
index af0751e02..48b7ae734 100755
--- a/src/conf_mode/protocols_babel.py
+++ b/src/conf_mode/protocols_babel.py
@@ -1,109 +1,110 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2021-2024 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.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.configverify import verify_access_list
 from vyos.configverify import verify_prefix_list
 from vyos.frrender import FRRender
 from vyos.utils.dict import dict_search
+from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'babel'):
         return None
 
     babel = config_dict['babel']
     babel['policy'] = config_dict['policy']
 
     # verify distribute_list
     if "distribute_list" in babel:
         acl_keys = {
             "ipv4": [
                 "distribute_list.ipv4.access_list.in",
                 "distribute_list.ipv4.access_list.out",
             ],
             "ipv6": [
                 "distribute_list.ipv6.access_list.in",
                 "distribute_list.ipv6.access_list.out",
             ]
         }
         prefix_list_keys = {
             "ipv4": [
                 "distribute_list.ipv4.prefix_list.in",
                 "distribute_list.ipv4.prefix_list.out",
             ],
             "ipv6":[
                 "distribute_list.ipv6.prefix_list.in",
                 "distribute_list.ipv6.prefix_list.out",
             ]
         }
         for address_family in ["ipv4", "ipv6"]:
             for iface_key in babel["distribute_list"].get(address_family, {}).get("interface", {}).keys():
                 acl_keys[address_family].extend([
                     f"distribute_list.{address_family}.interface.{iface_key}.access_list.in",
                     f"distribute_list.{address_family}.interface.{iface_key}.access_list.out"
                 ])
                 prefix_list_keys[address_family].extend([
                     f"distribute_list.{address_family}.interface.{iface_key}.prefix_list.in",
                     f"distribute_list.{address_family}.interface.{iface_key}.prefix_list.out"
                 ])
 
         for address_family, keys in acl_keys.items():
             for key in keys:
                 acl = dict_search(key, babel)
                 if acl:
                     verify_access_list(acl, babel, version='6' if address_family == 'ipv6' else '')
 
         for address_family, keys in prefix_list_keys.items():
             for key in keys:
                 prefix_list = dict_search(key, babel)
                 if prefix_list:
                     verify_prefix_list(prefix_list, babel, version='6' if address_family == 'ipv6' else '')
 
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_bfd.py b/src/conf_mode/protocols_bfd.py
index 623801897..2e7d40676 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -1,96 +1,97 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-2024 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 vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import verify_vrf
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.frrender import FRRender
 from vyos.template import is_ipv6
 from vyos.utils.network import is_ipv6_link_local
+from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'bfd'):
         return None
 
     bfd = config_dict['bfd']
     if 'peer' in bfd:
         for peer, peer_config in bfd['peer'].items():
             # IPv6 link local peers require an explicit local address/interface
             if is_ipv6_link_local(peer):
                 if 'source' not in peer_config or len(peer_config['source']) < 2:
                     raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting')
 
             # IPv6 peers require an explicit local address
             if is_ipv6(peer):
                 if 'source' not in peer_config or 'address' not in peer_config['source']:
                     raise ConfigError('BFD IPv6 peers require explicit local address setting')
 
             if 'multihop' in peer_config:
                 # multihop require source address
                 if 'source' not in peer_config or 'address' not in peer_config['source']:
                     raise ConfigError('BFD multihop require source address')
 
                 # multihop and echo-mode cannot be used together
                 if 'echo_mode' in peer_config:
                     raise ConfigError('BFD multihop and echo-mode cannot be used together')
 
                 # multihop doesn't accept interface names
                 if 'source' in peer_config and 'interface' in peer_config['source']:
                     raise ConfigError('BFD multihop and source interface cannot be used together')
 
             if 'minimum_ttl' in peer_config and 'multihop' not in peer_config:
                 raise ConfigError('Minimum TTL is only available for multihop BFD sessions!')
 
             if 'profile' in peer_config:
                 profile_name = peer_config['profile']
                 if 'profile' not in bfd or profile_name not in bfd['profile']:
                     raise ConfigError(f'BFD profile "{profile_name}" does not exist!')
 
             if 'vrf' in peer_config:
                 verify_vrf(peer_config)
 
     return None
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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 db3123bd3..ae32dd839 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -1,574 +1,575 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2020-2024 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 sys import argv
 
 from vyos.base import Warning
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.configverify import verify_prefix_list
 from vyos.configverify import verify_route_map
 from vyos.configverify import verify_vrf
 from vyos.frrender import FRRender
 from vyos.template import is_ip
 from vyos.template import is_interface
 from vyos.utils.dict import dict_search
 from vyos.utils.network import get_interface_vrf
 from vyos.utils.network import is_addr_assigned
+from vyos.utils.process import is_systemd_service_running
 from vyos.utils.process import process_named_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf, argv)
 
 def verify_vrf_as_import(search_vrf_name: str, afi_name: str, vrfs_config: dict) -> bool:
     """
     :param search_vrf_name: search vrf name in import list
     :type search_vrf_name: str
     :param afi_name: afi/safi name
     :type afi_name: str
     :param vrfs_config: configuration dependents vrfs
     :type vrfs_config: dict
     :return: if vrf in import list retrun true else false
     :rtype: bool
     """
     for vrf_name, vrf_config in vrfs_config.items():
         import_list = dict_search(
             f'protocols.bgp.address_family.{afi_name}.import.vrf',
             vrf_config)
         if import_list:
             if search_vrf_name in import_list:
                return True
     return False
 
 def verify_vrf_import_options(afi_config: dict) -> bool:
     """
     Search if afi contains one of options
     :param afi_config: afi/safi
     :type afi_config: dict
     :return: if vrf contains rd and route-target options return true else false
     :rtype: bool
     """
     options = [
         f'rd.vpn.export',
         f'route_target.vpn.import',
         f'route_target.vpn.export',
         f'route_target.vpn.both'
     ]
     for option in options:
         if dict_search(option, afi_config):
             return True
     return False
 
 def verify_vrf_import(vrf_name: str, vrfs_config: dict, afi_name: str) -> bool:
     """
     Verify if vrf exists and contain options
     :param vrf_name: name of VRF
     :type vrf_name: str
     :param vrfs_config: dependent vrfs config
     :type vrfs_config: dict
     :param afi_name: afi/safi name
     :type afi_name: str
     :return: if vrf contains rd and route-target options return true else false
     :rtype: bool
     """
     if vrf_name != 'default':
         verify_vrf({'vrf': vrf_name})
     if dict_search(f'{vrf_name}.protocols.bgp.address_family.{afi_name}',
                    vrfs_config):
         afi_config = \
         vrfs_config[vrf_name]['protocols']['bgp']['address_family'][
             afi_name]
         if verify_vrf_import_options(afi_config):
             return True
     return False
 
 def verify_vrflist_import(afi_name: str, afi_config: dict, vrfs_config: dict) -> bool:
     """
     Call function to verify
     if scpecific vrf contains rd and route-target
     options return true else false
 
     :param afi_name: afi/safi name
     :type afi_name: str
     :param afi_config: afi/safi configuration
     :type afi_config: dict
     :param vrfs_config: dependent vrfs config
     :type vrfs_config:dict
     :return: if vrf contains rd and route-target options return true else false
     :rtype: bool
     """
     for vrf_name in afi_config['import']['vrf']:
         if verify_vrf_import(vrf_name, vrfs_config, afi_name):
             return True
     return False
 
 def verify_remote_as(peer_config, bgp_config):
     if 'remote_as' in peer_config:
         return peer_config['remote_as']
 
     if 'peer_group' in peer_config:
         peer_group_name = peer_config['peer_group']
         tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', bgp_config)
         if tmp: return tmp
 
     if 'interface' in peer_config:
         if 'remote_as' in peer_config['interface']:
             return peer_config['interface']['remote_as']
 
         if 'peer_group' in peer_config['interface']:
             peer_group_name = peer_config['interface']['peer_group']
             tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', bgp_config)
             if tmp: return tmp
 
         if 'v6only' in peer_config['interface']:
             if 'remote_as' in peer_config['interface']['v6only']:
                 return peer_config['interface']['v6only']['remote_as']
             if 'peer_group' in peer_config['interface']['v6only']:
                 peer_group_name = peer_config['interface']['v6only']['peer_group']
                 tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', bgp_config)
                 if tmp: return tmp
 
     return None
 
 def verify_afi(peer_config, bgp_config):
     # If address_family configured under neighboor
     if 'address_family' in peer_config:
         return True
 
     # If address_family configured under peer-group
     # if neighbor interface configured
     peer_group_name = None
     if dict_search('interface.peer_group', peer_config):
         peer_group_name = peer_config['interface']['peer_group']
     elif dict_search('interface.v6only.peer_group', peer_config):
         peer_group_name = peer_config['interface']['v6only']['peer_group']
 
     # if neighbor IP configured.
     if 'peer_group' in peer_config:
         peer_group_name = peer_config['peer_group']
     if peer_group_name:
         tmp = dict_search(f'peer_group.{peer_group_name}.address_family', bgp_config)
         if tmp: return True
     return False
 
 def verify(config_dict):
 
     print('====== verify() ======')
     import pprint
     pprint.pprint(config_dict)
 
     if not has_frr_protocol_in_dict(config_dict, 'bgp'):
         return None
 
     vrf = None
     if 'vrf_context' in config_dict:
         vrf = config_dict['vrf_context']
 
     # eqivalent of the C foo ? 'a' : 'b' statement
     bgp = vrf and config_dict['vrf']['name'][vrf]['protocols']['bgp'] or config_dict['bgp']
     bgp['policy'] = config_dict['policy']
 
     if 'deleted' in bgp:
         if vrf:
             # Cannot delete vrf if it exists in import vrf list in other vrfs
             for tmp_afi in ['ipv4_unicast', 'ipv6_unicast']:
                 if verify_vrf_as_import(vrf, tmp_afi, bgp['dependent_vrfs']):
                     raise ConfigError(f'Cannot delete VRF instance "{vrf}", ' \
                                       'unconfigure "import vrf" commands!')
         else:
             # We are running in the default VRF context, thus we can not delete
             # our main BGP instance if there are dependent BGP VRF instances.
             if 'dependent_vrfs' in bgp:
                 for vrf, vrf_options in bgp['dependent_vrfs'].items():
                     if vrf != 'default':
                         if dict_search('protocols.bgp', vrf_options):
                             dependent_vrfs = ', '.join(bgp['dependent_vrfs'].keys())
                             raise ConfigError(f'Cannot delete default BGP instance, ' \
                                               f'dependent VRF instance(s): {dependent_vrfs}')
                         if 'vni' in vrf_options:
                             raise ConfigError('Cannot delete default BGP instance, ' \
                                               'dependent L3VNI exists!')
 
         return None
 
     if 'system_as' not in bgp:
         raise ConfigError('BGP system-as number must be defined!')
 
     # Verify BMP
     if 'bmp' in bgp:
         # check bmp flag "bgpd -d -F traditional --daemon -A 127.0.0.1 -M rpki -M bmp"
         if not process_named_running('bgpd', 'bmp'):
             raise ConfigError(
                 f'"bmp" flag is not found in bgpd. Configure "set system frr bmp" and restart bgp process'
             )
         # check bmp target
         if 'target' in bgp['bmp']:
             for target, target_config in bgp['bmp']['target'].items():
                 if 'address' not in target_config:
                     raise ConfigError(f'BMP target "{target}" address must be defined!')
 
     # Verify vrf on interface and bgp section
     if 'interface' in bgp:
         for interface in bgp['interface']:
             error_msg = f'Interface "{interface}" belongs to different VRF instance'
             tmp = get_interface_vrf(interface)
             if vrf:
                 if vrf != tmp:
                     raise ConfigError(f'{error_msg} "{vrf}"!')
             elif tmp != 'default':
                 raise ConfigError(f'{error_msg} "{tmp}"!')
 
     peer_groups_context = dict()
     # Common verification for both peer-group and neighbor statements
     for neighbor in ['neighbor', 'peer_group']:
         # bail out early if there is no neighbor or peer-group statement
         # this also saves one indention level
         if neighbor not in bgp:
             continue
 
         for peer, peer_config in bgp[neighbor].items():
             # Only regular "neighbor" statement can have a peer-group set
             # Check if the configure peer-group exists
             if 'peer_group' in peer_config:
                 peer_group = peer_config['peer_group']
                 if 'peer_group' not in bgp or peer_group not in bgp['peer_group']:
                     raise ConfigError(f'Specified peer-group "{peer_group}" for '\
                                       f'neighbor "{neighbor}" does not exist!')
 
                 if 'remote_as' in peer_config:
                     is_ibgp = True
                     if peer_config['remote_as'] != 'internal' and \
                             peer_config['remote_as'] != bgp['system_as']:
                         is_ibgp = False
 
                     if peer_group not in peer_groups_context:
                         peer_groups_context[peer_group] = is_ibgp
                     elif peer_groups_context[peer_group] != is_ibgp:
                         raise ConfigError(f'Peer-group members must be '
                                           f'all internal or all external')
 
             if 'local_role' in peer_config:
                 #Ensure Local Role has only one value.
                 if len(peer_config['local_role']) > 1:
                     raise ConfigError(f'Only one local role can be specified for peer "{peer}"!')
 
             if 'local_as' in peer_config:
                 if len(peer_config['local_as']) > 1:
                     raise ConfigError(f'Only one local-as number can be specified for peer "{peer}"!')
 
                 # Neighbor local-as override can not be the same as the local-as
                 # we use for this BGP instane!
                 asn = list(peer_config['local_as'].keys())[0]
                 if asn == bgp['system_as']:
                     raise ConfigError('Cannot have local-as same as system-as number')
 
                 # Neighbor AS specified for local-as and remote-as can not be the same
                 if dict_search('remote_as', peer_config) == asn and neighbor != 'peer_group':
                      raise ConfigError(f'Neighbor "{peer}" has local-as specified which is '\
                                         'the same as remote-as, this is not allowed!')
 
             # ttl-security and ebgp-multihop can't be used in the same configration
             if 'ebgp_multihop' in peer_config and 'ttl_security' in peer_config:
                 raise ConfigError('You can not set both ebgp-multihop and ttl-security hops')
 
             # interface and ebgp-multihop can't be used in the same configration
             if 'ebgp_multihop' in peer_config and 'interface' in peer_config:
                 raise ConfigError(f'Ebgp-multihop can not be used with directly connected '\
                                   f'neighbor "{peer}"')
 
             # Check if neighbor has both override capability and strict capability match
             # configured at the same time.
             if 'override_capability' in peer_config and 'strict_capability_match' in peer_config:
                 raise ConfigError(f'Neighbor "{peer}" cannot have both override-capability and '\
                                   'strict-capability-match configured at the same time!')
 
             # Check spaces in the password
             if 'password' in peer_config and ' ' in peer_config['password']:
                 raise ConfigError('Whitespace is not allowed in passwords!')
 
             # Some checks can/must only be done on a neighbor and not a peer-group
             if neighbor == 'neighbor':
                 # remote-as must be either set explicitly for the neighbor
                 # or for the entire peer-group
                 if not verify_remote_as(peer_config, bgp):
                     raise ConfigError(f'Neighbor "{peer}" remote-as must be set!')
 
                 if not verify_afi(peer_config, bgp):
                     Warning(f'BGP neighbor "{peer}" requires address-family!')
 
                 # Peer-group member cannot override remote-as of peer-group
                 if 'peer_group' in peer_config:
                     peer_group = peer_config['peer_group']
                     if 'remote_as' in peer_config and 'remote_as' in bgp['peer_group'][peer_group]:
                         raise ConfigError(f'Peer-group member "{peer}" cannot override remote-as of peer-group "{peer_group}"!')
                 if 'interface' in peer_config:
                     if 'peer_group' in peer_config['interface']:
                         peer_group = peer_config['interface']['peer_group']
                         if 'remote_as' in peer_config['interface'] and 'remote_as' in bgp['peer_group'][peer_group]:
                             raise ConfigError(f'Peer-group member "{peer}" cannot override remote-as of peer-group "{peer_group}"!')
                     if 'v6only' in peer_config['interface']:
                         if 'peer_group' in peer_config['interface']['v6only']:
                             peer_group = peer_config['interface']['v6only']['peer_group']
                             if 'remote_as' in peer_config['interface']['v6only'] and 'remote_as' in bgp['peer_group'][peer_group]:
                                 raise ConfigError(f'Peer-group member "{peer}" cannot override remote-as of peer-group "{peer_group}"!')
 
                 # Only checks for ipv4 and ipv6 neighbors
                 # Check if neighbor address is assigned as system interface address
                 vrf_error_msg = f' in default VRF!'
                 if vrf:
                     vrf_error_msg = f' in VRF "{vrf}"!'
 
                 if is_ip(peer) and is_addr_assigned(peer, vrf):
                     raise ConfigError(f'Can not configure local address as neighbor "{peer}"{vrf_error_msg}')
                 elif is_interface(peer):
                     if 'peer_group' in peer_config:
                         raise ConfigError(f'peer-group must be set under the interface node of "{peer}"')
                     if 'remote_as' in peer_config:
                         raise ConfigError(f'remote-as must be set under the interface node of "{peer}"')
                     if 'source_interface' in peer_config['interface']:
                         raise ConfigError(f'"source-interface" option not allowed for neighbor "{peer}"')
 
             # Local-AS allowed only for EBGP peers
             if 'local_as' in peer_config:
                 remote_as = verify_remote_as(peer_config, bgp)
                 if remote_as == bgp['system_as']:
                     raise ConfigError(f'local-as configured for "{peer}", allowed only for eBGP peers!')
 
             for afi in ['ipv4_unicast', 'ipv4_multicast', 'ipv4_labeled_unicast', 'ipv4_flowspec',
                         'ipv6_unicast', 'ipv6_multicast', 'ipv6_labeled_unicast', 'ipv6_flowspec',
                         'l2vpn_evpn']:
                 # Bail out early if address family is not configured
                 if 'address_family' not in peer_config or afi not in peer_config['address_family']:
                     continue
 
                 # Check if neighbor has both ipv4 unicast and ipv4 labeled unicast configured at the same time.
                 if 'ipv4_unicast' in peer_config['address_family'] and 'ipv4_labeled_unicast' in peer_config['address_family']:
                     raise ConfigError(f'Neighbor "{peer}" cannot have both ipv4-unicast and ipv4-labeled-unicast configured at the same time!')
 
                 # Check if neighbor has both ipv6 unicast and ipv6 labeled unicast configured at the same time.
                 if 'ipv6_unicast' in peer_config['address_family'] and 'ipv6_labeled_unicast' in peer_config['address_family']:
                     raise ConfigError(f'Neighbor "{peer}" cannot have both ipv6-unicast and ipv6-labeled-unicast configured at the same time!')
 
                 afi_config = peer_config['address_family'][afi]
 
                 if 'conditionally_advertise' in afi_config:
                     if 'advertise_map' not in afi_config['conditionally_advertise']:
                         raise ConfigError('Must speficy advertise-map when conditionally-advertise is in use!')
                     # Verify advertise-map (which is a route-map) exists
                     verify_route_map(afi_config['conditionally_advertise']['advertise_map'], bgp)
 
                     if ('exist_map' not in afi_config['conditionally_advertise'] and
                         'non_exist_map' not in afi_config['conditionally_advertise']):
                         raise ConfigError('Must either speficy exist-map or non-exist-map when ' \
                                           'conditionally-advertise is in use!')
 
                     if {'exist_map', 'non_exist_map'} <= set(afi_config['conditionally_advertise']):
                         raise ConfigError('Can not specify both exist-map and non-exist-map for ' \
                                           'conditionally-advertise!')
 
                     if 'exist_map' in afi_config['conditionally_advertise']:
                         verify_route_map(afi_config['conditionally_advertise']['exist_map'], bgp)
 
                     if 'non_exist_map' in afi_config['conditionally_advertise']:
                         verify_route_map(afi_config['conditionally_advertise']['non_exist_map'], bgp)
 
                 # T4332: bgp deterministic-med cannot be disabled while addpath-tx-bestpath-per-AS is in use
                 if 'addpath_tx_per_as' in afi_config:
                     if dict_search('parameters.deterministic_med', bgp) == None:
                         raise ConfigError('addpath-tx-per-as requires BGP deterministic-med paramtere to be set!')
 
                 # Validate if configured Prefix list exists
                 if 'prefix_list' in afi_config:
                     for tmp in ['import', 'export']:
                         if tmp not in afi_config['prefix_list']:
                             # bail out early
                             continue
                         if afi == 'ipv4_unicast':
                             verify_prefix_list(afi_config['prefix_list'][tmp], bgp)
                         elif afi == 'ipv6_unicast':
                             verify_prefix_list(afi_config['prefix_list'][tmp], bgp, version='6')
 
                 if 'route_map' in afi_config:
                     for tmp in ['import', 'export']:
                         if tmp in afi_config['route_map']:
                             verify_route_map(afi_config['route_map'][tmp], bgp)
 
                 if 'route_reflector_client' in afi_config:
                     peer_group_as = peer_config.get('remote_as')
 
                     if peer_group_as is None or (peer_group_as != 'internal' and peer_group_as != bgp['system_as']):
                         raise ConfigError('route-reflector-client only supported for iBGP peers')
                     else:
                         if 'peer_group' in peer_config:
                             peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', bgp)
                             if peer_group_as is None or (peer_group_as != 'internal' and peer_group_as != bgp['system_as']):
                                 raise ConfigError('route-reflector-client only supported for iBGP peers')
 
             # T5833 not all AFIs are supported for VRF
             if 'vrf' in bgp and 'address_family' in peer_config:
                 unsupported_vrf_afi = {
                     'ipv4_flowspec',
                     'ipv6_flowspec',
                     'ipv4_labeled_unicast',
                     'ipv6_labeled_unicast',
                     'ipv4_vpn',
                     'ipv6_vpn',
                 }
                 for afi in peer_config['address_family']:
                     if afi in unsupported_vrf_afi:
                         raise ConfigError(
                             f"VRF is not allowed for address-family '{afi.replace('_', '-')}'"
                         )
 
     # Throw an error if a peer group is not configured for allow range
     for prefix in dict_search('listen.range', bgp) or []:
         # we can not use dict_search() here as prefix contains dots ...
         if 'peer_group' not in bgp['listen']['range'][prefix]:
             raise ConfigError(f'Listen range for prefix "{prefix}" has no peer group configured.')
 
         peer_group = bgp['listen']['range'][prefix]['peer_group']
         if 'peer_group' not in bgp or peer_group not in bgp['peer_group']:
             raise ConfigError(f'Peer-group "{peer_group}" for listen range "{prefix}" does not exist!')
 
         if not verify_remote_as(bgp['listen']['range'][prefix], bgp):
             raise ConfigError(f'Peer-group "{peer_group}" requires remote-as to be set!')
 
     # Throw an error if the global administrative distance parameters aren't all filled out.
     if dict_search('parameters.distance.global', bgp) != None:
         for key in ['external', 'internal', 'local']:
             if dict_search(f'parameters.distance.global.{key}', bgp) == None:
                 raise ConfigError('Missing mandatory configuration option for '\
                                  f'global administrative distance {key}!')
 
     # TCP keepalive requires all three parameters to be set
     if dict_search('parameters.tcp_keepalive', bgp) != None:
         if not {'idle', 'interval', 'probes'} <= set(bgp['parameters']['tcp_keepalive']):
             raise ConfigError('TCP keepalive incomplete - idle, keepalive and probes must be set')
 
     # Address Family specific validation
     if 'address_family' in bgp:
         for afi, afi_config in bgp['address_family'].items():
             if 'distance' in afi_config:
                 # Throw an error if the address family specific administrative
                 # distance parameters aren't all filled out.
                 for key in ['external', 'internal', 'local']:
                     if key not in afi_config['distance']:
                         raise ConfigError('Missing mandatory configuration option for '\
                                          f'{afi} administrative distance {key}!')
 
             if afi in ['ipv4_unicast', 'ipv6_unicast']:
                 vrf_name = vrf if vrf else 'default'
                 # Verify if currant VRF contains rd and route-target options
                 # and does not exist in import list in other VRFs
                 if dict_search(f'rd.vpn.export', afi_config):
                     if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']):
                         raise ConfigError(
                             'Command "import vrf" conflicts with "rd vpn export" command!')
                     if not dict_search('parameters.router_id', bgp):
                         Warning(f'BGP "router-id" is required when using "rd" and "route-target"!')
 
                 if dict_search('route_target.vpn.both', afi_config):
                     if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']):
                         raise ConfigError(
                             'Command "import vrf" conflicts with "route-target vpn both" command!')
                     if dict_search('route_target.vpn.export', afi_config):
                         raise ConfigError(
                             'Command "route-target vpn export" conflicts '\
                             'with "route-target vpn both" command!')
                     if dict_search('route_target.vpn.import', afi_config):
                         raise ConfigError(
                             'Command "route-target vpn import" conflicts '\
                             'with "route-target vpn both" command!')
 
                 if dict_search('route_target.vpn.import', afi_config):
                     if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']):
                         raise ConfigError(
                             'Command "import vrf conflicts" with "route-target vpn import" command!')
 
                 if dict_search('route_target.vpn.export', afi_config):
                     if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']):
                         raise ConfigError(
                             'Command "import vrf" conflicts with "route-target vpn export" command!')
 
                 # Verify if VRFs in import do not contain rd
                 # and route-target options
                 if dict_search('import.vrf', afi_config) is not None:
                     # Verify if VRF with import does not contain rd
                     # and route-target options
                     if verify_vrf_import_options(afi_config):
                         raise ConfigError(
                             'Please unconfigure "import vrf" commands before using vpn commands in the same VRF!')
                     # Verify if VRFs in import list do not contain rd
                     # and route-target options
                     if verify_vrflist_import(afi, afi_config, bgp['dependent_vrfs']):
                         raise ConfigError(
                             'Please unconfigure import vrf commands before using vpn commands in dependent VRFs!')
 
                     # FRR error: please unconfigure vpn to vrf commands before
                     # using import vrf commands
                     if 'vpn' in afi_config['import'] or dict_search('export.vpn', afi_config) != None:
                         raise ConfigError('Please unconfigure VPN to VRF commands before '\
                                           'using "import vrf" commands!')
 
                 # Verify that the export/import route-maps do exist
                 for export_import in ['export', 'import']:
                     tmp = dict_search(f'route_map.vpn.{export_import}', afi_config)
                     if tmp: verify_route_map(tmp, bgp)
 
                 # per-vrf sid and per-af sid are mutually exclusive
                 if 'sid' in afi_config and 'sid' in bgp:
                     raise ConfigError('SID per VRF and SID per address-family are mutually exclusive!')
 
             # Checks only required for L2VPN EVPN
             if afi in ['l2vpn_evpn']:
                 if 'vni' in afi_config:
                     for vni, vni_config in afi_config['vni'].items():
                         if 'rd' in vni_config and 'advertise_all_vni' not in afi_config:
                             raise ConfigError('BGP EVPN "rd" requires "advertise-all-vni" to be set!')
                         if 'route_target' in vni_config and 'advertise_all_vni' not in afi_config:
                             raise ConfigError('BGP EVPN "route-target" requires "advertise-all-vni" to be set!')
 
     return None
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_eigrp.py b/src/conf_mode/protocols_eigrp.py
index 33812d350..8f49bb151 100755
--- a/src/conf_mode/protocols_eigrp.py
+++ b/src/conf_mode/protocols_eigrp.py
@@ -1,73 +1,74 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2022-2024 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 sys import argv
 
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.configverify import verify_vrf
+from vyos.utils.process import is_systemd_service_running
 from vyos.frrender import FRRender
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf, argv)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'eigrp'):
         return None
 
     vrf = None
     if 'vrf_context' in config_dict:
         vrf = config_dict['vrf_context']
 
     # eqivalent of the C foo ? 'a' : 'b' statement
     eigrp = vrf and config_dict['vrf']['name'][vrf]['protocols']['eigrp'] or config_dict['eigrp']
     eigrp['policy'] = config_dict['policy']
 
     if 'system_as' not in eigrp:
         raise ConfigError('EIGRP system-as must be defined!')
 
     if vrf:
         verify_vrf({'vrf': vrf})
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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 7a54685bb..1e5f0d6e8 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -1,252 +1,253 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2020-2024 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 sys import argv
 
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.configverify import verify_common_route_maps
 from vyos.configverify import verify_interface_exists
 from vyos.frrender import FRRender
 from vyos.ifconfig import Interface
 from vyos.utils.dict import dict_search
 from vyos.utils.network import get_interface_config
+from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf, argv)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'isis'):
         return None
 
     vrf = None
     if 'vrf_context' in config_dict:
         vrf = config_dict['vrf_context']
 
     # eqivalent of the C foo ? 'a' : 'b' statement
     isis = vrf and config_dict['vrf']['name'][vrf]['protocols']['isis'] or config_dict['isis']
     isis['policy'] = config_dict['policy']
 
     if 'deleted' in isis:
         return None
 
     if 'net' not in isis:
         raise ConfigError('Network entity is mandatory!')
 
     # last byte in IS-IS area address must be 0
     tmp = isis['net'].split('.')
     if int(tmp[-1]) != 0:
         raise ConfigError('Last byte of IS-IS network entity title must always be 0!')
 
     verify_common_route_maps(isis)
 
     # If interface not set
     if 'interface' not in isis:
         raise ConfigError('Interface used for routing updates is mandatory!')
 
     for interface in isis['interface']:
         verify_interface_exists(isis, interface)
         # Interface MTU must be >= configured lsp-mtu
         mtu = Interface(interface).get_mtu()
         area_mtu = isis['lsp_mtu']
         # Recommended maximum PDU size = interface MTU - 3 bytes
         recom_area_mtu = mtu - 3
         if mtu < int(area_mtu) or int(area_mtu) > recom_area_mtu:
             raise ConfigError(f'Interface {interface} has MTU {mtu}, ' \
                               f'current area MTU is {area_mtu}! \n' \
                               f'Recommended area lsp-mtu {recom_area_mtu} or less ' \
                               '(calculated on MTU size).')
 
         if vrf:
             # If interface specific options are set, we must ensure that the
             # interface is bound to our requesting VRF. Due to the VyOS
             # priorities the interface is bound to the VRF after creation of
             # the VRF itself, and before any routing protocol is configured.
             tmp = get_interface_config(interface)
             if 'master' not in tmp or tmp['master'] != vrf:
                 raise ConfigError(f'Interface "{interface}" is not a member of VRF "{vrf}"!')
 
     # If md5 and plaintext-password set at the same time
     for password in ['area_password', 'domain_password']:
         if password in isis:
             if {'md5', 'plaintext_password'} <= set(isis[password]):
                 tmp = password.replace('_', '-')
                 raise ConfigError(f'Can use either md5 or plaintext-password for {tmp}!')
 
     # If one param from delay set, but not set others
     if 'spf_delay_ietf' in isis:
         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['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 spf-delay must be configured. Missing: ' + ', '.join(exist_timers).replace('_', '-'))
 
     # If Redistribute set, but level don't set
     if 'redistribute' in isis:
         proc_level = isis.get('level','').replace('-','_')
         for afi in ['ipv4', 'ipv6']:
             if afi not in isis['redistribute']:
                 continue
 
             for proto, proto_config in isis['redistribute'][afi].items():
                 if 'level_1' not in proto_config and 'level_2' not in proto_config:
                     raise ConfigError(f'Redistribute level-1 or level-2 should be specified in ' \
                                       f'"protocols isis redistribute {afi} {proto}"!')
 
                 for redistr_level, redistr_config in proto_config.items():
                     if proc_level and proc_level != 'level_1_2' and proc_level != redistr_level:
                         raise ConfigError(f'"protocols isis redistribute {afi} {proto} {redistr_level}" ' \
                                           f'can not be used with \"protocols isis level {proc_level}\"!')
 
     # Segment routing checks
     if dict_search('segment_routing.global_block', isis):
         g_high_label_value = dict_search('segment_routing.global_block.high_label_value', isis)
         g_low_label_value = dict_search('segment_routing.global_block.low_label_value', isis)
 
         # If segment routing global block high or low value is blank, throw error
         if not (g_low_label_value or g_high_label_value):
             raise ConfigError('Segment routing global-block requires both low and high value!')
 
         # If segment routing global block low value is higher than the high value, throw error
         if int(g_low_label_value) > int(g_high_label_value):
             raise ConfigError('Segment routing global-block low value must be lower than high value')
 
     if dict_search('segment_routing.local_block', isis):
         if dict_search('segment_routing.global_block', isis) == None:
             raise ConfigError('Segment routing local-block requires global-block to be configured!')
 
         l_high_label_value = dict_search('segment_routing.local_block.high_label_value', isis)
         l_low_label_value = dict_search('segment_routing.local_block.low_label_value', isis)
 
         # If segment routing local-block high or low value is blank, throw error
         if not (l_low_label_value or l_high_label_value):
             raise ConfigError('Segment routing local-block requires both high and low value!')
 
         # If segment routing local-block low value is higher than the high value, throw error
         if int(l_low_label_value) > int(l_high_label_value):
             raise ConfigError('Segment routing local-block low value must be lower than high value')
 
         # local-block most live outside global block
         global_range = range(int(g_low_label_value), int(g_high_label_value) +1)
         local_range  = range(int(l_low_label_value), int(l_high_label_value) +1)
 
         # Check for overlapping ranges
         if list(set(global_range) & set(local_range)):
             raise ConfigError(f'Segment-Routing Global Block ({g_low_label_value}/{g_high_label_value}) '\
                               f'conflicts with Local Block ({l_low_label_value}/{l_high_label_value})!')
 
     # Check for a blank or invalid value per prefix
     if dict_search('segment_routing.prefix', isis):
         for prefix, prefix_config in isis['segment_routing']['prefix'].items():
             if 'absolute' in prefix_config:
                 if prefix_config['absolute'].get('value') is None:
                     raise ConfigError(f'Segment routing prefix {prefix} absolute value cannot be blank.')
             elif 'index' in prefix_config:
                 if prefix_config['index'].get('value') is None:
                     raise ConfigError(f'Segment routing prefix {prefix} index value cannot be blank.')
 
     # Check for explicit-null and no-php-flag configured at the same time per prefix
     if dict_search('segment_routing.prefix', isis):
         for prefix, prefix_config in isis['segment_routing']['prefix'].items():
             if 'absolute' in prefix_config:
                 if ("explicit_null" in prefix_config['absolute']) and ("no_php_flag" in prefix_config['absolute']):
                     raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\
                                       f'and no-php-flag configured at the same time.')
             elif 'index' in prefix_config:
                 if ("explicit_null" in prefix_config['index']) and ("no_php_flag" in prefix_config['index']):
                     raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\
                                       f'and no-php-flag configured at the same time.')
 
     # Check for index ranges being larger than the segment routing global block
     if dict_search('segment_routing.global_block', isis):
         g_high_label_value = dict_search('segment_routing.global_block.high_label_value', isis)
         g_low_label_value = dict_search('segment_routing.global_block.low_label_value', isis)
         g_label_difference = int(g_high_label_value) - int(g_low_label_value)
         if dict_search('segment_routing.prefix', isis):
             for prefix, prefix_config in isis['segment_routing']['prefix'].items():
                 if 'index' in prefix_config:
                     index_size = isis['segment_routing']['prefix'][prefix]['index']['value']
                     if int(index_size) > int(g_label_difference):
                         raise ConfigError(f'Segment routing prefix {prefix} cannot have an '\
                                           f'index base size larger than the SRGB label base.')
 
     # Check for LFA tiebreaker index duplication
     if dict_search('fast_reroute.lfa.local.tiebreaker', isis):
         comparison_dictionary = {}
         for item, item_options in isis['fast_reroute']['lfa']['local']['tiebreaker'].items():
             for index, index_options in item_options.items():
                 for index_value, index_value_options in index_options.items():
                     if index_value not in comparison_dictionary.keys():
                         comparison_dictionary[index_value] = [item]
                     else:
                         comparison_dictionary[index_value].append(item)
         for index, index_length in comparison_dictionary.items():
             if int(len(index_length)) > 1:
                 raise ConfigError(f'LFA index {index} cannot have more than one tiebreaker configured.')
 
     # Check for LFA priority-limit configured multiple times per level
     if dict_search('fast_reroute.lfa.local.priority_limit', isis):
         comparison_dictionary = {}
         for priority, priority_options in isis['fast_reroute']['lfa']['local']['priority_limit'].items():
             for level, level_options in priority_options.items():
                 if level not in comparison_dictionary.keys():
                     comparison_dictionary[level] = [priority]
                 else:
                     comparison_dictionary[level].append(priority)
             for level, level_length in comparison_dictionary.items():
                 if int(len(level_length)) > 1:
                     raise ConfigError(f'LFA priority-limit on {level.replace("_", "-")} cannot have more than one priority configured.')
 
     # Check for LFA remote prefix list configured with more than one list
     if dict_search('fast_reroute.lfa.remote.prefix_list', isis):
         if int(len(isis['fast_reroute']['lfa']['remote']['prefix_list'].items())) > 1:
             raise ConfigError(f'LFA remote prefix-list has more than one configured. Cannot have more than one configured.')
 
     return None
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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 bab9648c4..e8097b7ff 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -1,139 +1,140 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2020-2024 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 glob import glob
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.frrender import FRRender
 from vyos.utils.dict import dict_search
 from vyos.utils.file import read_file
+from vyos.utils.process import is_systemd_service_running
 from vyos.utils.system import sysctl_write
 from vyos.configverify import verify_interface_exists
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'mpls'):
         return None
 
     mpls = config_dict['mpls']
 
     if 'interface' in mpls:
         for interface in mpls['interface']:
             verify_interface_exists(mpls, interface)
 
     # 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(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
 
     if not has_frr_protocol_in_dict(config_dict, 'mpls'):
         return None
 
     mpls = config_dict['mpls']
 
     # Set number of entries in the platform label tables
     labels = '0'
     if 'interface' in mpls:
         labels = '1048575'
     sysctl_write('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']:
             sysctl_write('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']
             sysctl_write('net.mpls.default_ttl', ttl)
     else:
         # Set default global MPLS options if not defined.
         sysctl_write('net.mpls.ip_ttl_propagate', 1)
         sysctl_write('net.mpls.default_ttl', 255)
 
     # Enable and disable MPLS processing on interfaces per configuration
     if 'interface' in mpls:
         system_interfaces = []
         # Populate system interfaces list with local MPLS capable interfaces
         for interface in glob('/proc/sys/net/mpls/conf/*'):
             system_interfaces.append(os.path.basename(interface))
         # This is where the comparison is done on if an interface needs to be enabled/disabled.
         for system_interface in system_interfaces:
             interface_state = read_file(f'/proc/sys/net/mpls/conf/{system_interface}/input')
             if '1' in interface_state:
                 if system_interface not in mpls['interface']:
                     system_interface = system_interface.replace('.', '/')
                     sysctl_write(f'net.mpls.conf.{system_interface}.input', 0)
             elif '0' in interface_state:
                 if system_interface in mpls['interface']:
                     system_interface = system_interface.replace('.', '/')
                     sysctl_write(f'net.mpls.conf.{system_interface}.input', 1)
     else:
         system_interfaces = []
         # If MPLS interfaces are not configured, set MPLS processing disabled
         for interface in glob('/proc/sys/net/mpls/conf/*'):
             system_interfaces.append(os.path.basename(interface))
         for system_interface in system_interfaces:
             system_interface = system_interface.replace('.', '/')
             sysctl_write(f'net.mpls.conf.{system_interface}.input', 0)
 
     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_openfabric.py b/src/conf_mode/protocols_openfabric.py
index 48c89d742..41c5d9544 100644
--- a/src/conf_mode/protocols_openfabric.py
+++ b/src/conf_mode/protocols_openfabric.py
@@ -1,109 +1,110 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2024 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.base import Warning
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import verify_interface_exists
 from vyos.configverify import has_frr_protocol_in_dict
+from vyos.utils.process import is_systemd_service_running
 from vyos.frrender import FRRender
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'openfabric'):
         return None
 
     openfabric = config_dict['openfabric']
     if 'deleted' in openfabric:
         return None
 
     if 'net' not in openfabric:
         raise ConfigError('Network entity is mandatory!')
 
     # last byte in OpenFabric area address must be 0
     tmp = openfabric['net'].split('.')
     if int(tmp[-1]) != 0:
         raise ConfigError('Last byte of OpenFabric network entity title must always be 0!')
 
     if 'domain' not in openfabric:
         raise ConfigError('OpenFabric domain name is mandatory!')
 
     interfaces_used = []
 
     for domain, domain_config in openfabric['domain'].items():
         # If interface not set
         if 'interface' not in domain_config:
             raise ConfigError(f'Interface used for routing updates in OpenFabric "{domain}" is mandatory!')
 
         for iface, iface_config in domain_config['interface'].items():
             verify_interface_exists(openfabric, iface)
 
             # interface can be activated only on one OpenFabric instance
             if iface in interfaces_used:
                 raise ConfigError(f'Interface {iface} is already used in different OpenFabric instance!')
 
             if 'address_family' not in iface_config or len(iface_config['address_family']) < 1:
                 raise ConfigError(f'Need to specify address family for the interface "{iface}"!')
 
             # If md5 and plaintext-password set at the same time
             if 'password' in iface_config:
                 if {'md5', 'plaintext_password'} <= set(iface_config['password']):
                     raise ConfigError(f'Can use either md5 or plaintext-password for password for the interface!')
 
             if iface == 'lo' and 'passive' not in iface_config:
                 Warning('For loopback interface passive mode is implied!')
 
             interfaces_used.append(iface)
 
         # If md5 and plaintext-password set at the same time
         password = 'domain_password'
         if password in domain_config:
             if {'md5', 'plaintext_password'} <= set(domain_config[password]):
                 raise ConfigError(f'Can use either md5 or plaintext-password for domain-password!')
 
     return None
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_ospf.py b/src/conf_mode/protocols_ospf.py
index 9d35aa007..f2c95a63c 100755
--- a/src/conf_mode/protocols_ospf.py
+++ b/src/conf_mode/protocols_ospf.py
@@ -1,196 +1,197 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2021-2024 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 sys import argv
 
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import verify_common_route_maps
 from vyos.configverify import verify_route_map
 from vyos.configverify import verify_interface_exists
 from vyos.configverify import verify_access_list
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.frrender import FRRender
 from vyos.utils.dict import dict_search
 from vyos.utils.network import get_interface_config
+from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf, argv)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'ospf'):
         return None
 
     vrf = None
     if 'vrf_context' in config_dict:
         vrf = config_dict['vrf_context']
 
     # eqivalent of the C foo ? 'a' : 'b' statement
     ospf = vrf and config_dict['vrf']['name'][vrf]['protocols']['ospf'] or config_dict['ospf']
     ospf['policy'] = config_dict['policy']
 
     verify_common_route_maps(ospf)
 
     # As we can have a default-information route-map, we need to validate it!
     route_map_name = dict_search('default_information.originate.route_map', ospf)
     if route_map_name: verify_route_map(route_map_name, ospf)
 
     # Validate if configured Access-list exists
     if 'area' in ospf:
           networks = []
           for area, area_config in ospf['area'].items():
               if 'import_list' in area_config:
                   acl_import = area_config['import_list']
                   if acl_import: verify_access_list(acl_import, ospf)
               if 'export_list' in area_config:
                   acl_export = area_config['export_list']
                   if acl_export: verify_access_list(acl_export, ospf)
 
               if 'network' in area_config:
                   for network in area_config['network']:
                       if network in networks:
                           raise ConfigError(f'Network "{network}" already defined in different area!')
                       networks.append(network)
 
     if 'interface' in ospf:
         for interface, interface_config in ospf['interface'].items():
             verify_interface_exists(ospf, interface)
             # One can not use dead-interval and hello-multiplier at the same
             # time. FRR will only activate the last option set via CLI.
             if {'hello_multiplier', 'dead_interval'} <= set(interface_config):
                 raise ConfigError(f'Can not use hello-multiplier and dead-interval ' \
                                   f'concurrently for {interface}!')
 
             # One can not use the "network <prefix> area <id>" command and an
             # per interface area assignment at the same time. FRR will error
             # out using: "Please remove all network commands first."
             if 'area' in ospf and 'area' in interface_config:
                 for area, area_config in ospf['area'].items():
                     if 'network' in area_config:
                         raise ConfigError('Can not use OSPF interface area and area ' \
                                           'network configuration at the same time!')
 
             # If interface specific options are set, we must ensure that the
             # interface is bound to our requesting VRF. Due to the VyOS
             # priorities the interface is bound to the VRF after creation of
             # the VRF itself, and before any routing protocol is configured.
             if vrf:
                 tmp = get_interface_config(interface)
                 if 'master' not in tmp or tmp['master'] != vrf:
                     raise ConfigError(f'Interface "{interface}" is not a member of VRF "{vrf}"!')
 
     # Segment routing checks
     if dict_search('segment_routing.global_block', ospf):
         g_high_label_value = dict_search('segment_routing.global_block.high_label_value', ospf)
         g_low_label_value = dict_search('segment_routing.global_block.low_label_value', ospf)
 
         # If segment routing global block high or low value is blank, throw error
         if not (g_low_label_value or g_high_label_value):
             raise ConfigError('Segment routing global-block requires both low and high value!')
 
         # If segment routing global block low value is higher than the high value, throw error
         if int(g_low_label_value) > int(g_high_label_value):
             raise ConfigError('Segment routing global-block low value must be lower than high value')
 
     if dict_search('segment_routing.local_block', ospf):
         if dict_search('segment_routing.global_block', ospf) == None:
             raise ConfigError('Segment routing local-block requires global-block to be configured!')
 
         l_high_label_value = dict_search('segment_routing.local_block.high_label_value', ospf)
         l_low_label_value = dict_search('segment_routing.local_block.low_label_value', ospf)
 
         # If segment routing local-block high or low value is blank, throw error
         if not (l_low_label_value or l_high_label_value):
             raise ConfigError('Segment routing local-block requires both high and low value!')
 
         # If segment routing local-block low value is higher than the high value, throw error
         if int(l_low_label_value) > int(l_high_label_value):
             raise ConfigError('Segment routing local-block low value must be lower than high value')
 
         # local-block most live outside global block
         global_range = range(int(g_low_label_value), int(g_high_label_value) +1)
         local_range  = range(int(l_low_label_value), int(l_high_label_value) +1)
 
         # Check for overlapping ranges
         if list(set(global_range) & set(local_range)):
             raise ConfigError(f'Segment-Routing Global Block ({g_low_label_value}/{g_high_label_value}) '\
                               f'conflicts with Local Block ({l_low_label_value}/{l_high_label_value})!')
 
     # Check for a blank or invalid value per prefix
     if dict_search('segment_routing.prefix', ospf):
         for prefix, prefix_config in ospf['segment_routing']['prefix'].items():
             if 'index' in prefix_config:
                 if prefix_config['index'].get('value') is None:
                     raise ConfigError(f'Segment routing prefix {prefix} index value cannot be blank.')
 
     # Check for explicit-null and no-php-flag configured at the same time per prefix
     if dict_search('segment_routing.prefix', ospf):
         for prefix, prefix_config in ospf['segment_routing']['prefix'].items():
             if 'index' in prefix_config:
                 if ("explicit_null" in prefix_config['index']) and ("no_php_flag" in prefix_config['index']):
                     raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\
                                       f'and no-php-flag configured at the same time.')
 
     # Check for index ranges being larger than the segment routing global block
     if dict_search('segment_routing.global_block', ospf):
         g_high_label_value = dict_search('segment_routing.global_block.high_label_value', ospf)
         g_low_label_value = dict_search('segment_routing.global_block.low_label_value', ospf)
         g_label_difference = int(g_high_label_value) - int(g_low_label_value)
         if dict_search('segment_routing.prefix', ospf):
             for prefix, prefix_config in ospf['segment_routing']['prefix'].items():
                 if 'index' in prefix_config:
                     index_size = ospf['segment_routing']['prefix'][prefix]['index']['value']
                     if int(index_size) > int(g_label_difference):
                         raise ConfigError(f'Segment routing prefix {prefix} cannot have an '\
                                           f'index base size larger than the SRGB label base.')
 
     # Check route summarisation
     if 'summary_address' in ospf:
         for prefix, prefix_options in ospf['summary_address'].items():
             if {'tag', 'no_advertise'} <= set(prefix_options):
                 raise ConfigError(f'Can not set both "tag" and "no-advertise" for Type-5 '\
                                   f'and Type-7 route summarisation of "{prefix}"!')
 
     return None
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
index c6b042b54..ac189c378 100755
--- a/src/conf_mode/protocols_ospfv3.py
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -1,107 +1,108 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2021-2024 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 sys import argv
 
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import verify_common_route_maps
 from vyos.configverify import verify_route_map
 from vyos.configverify import verify_interface_exists
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.frrender import FRRender
 from vyos.ifconfig import Interface
 from vyos.utils.dict import dict_search
 from vyos.utils.network import get_interface_config
+from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf, argv)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'ospfv3'):
         return None
 
     vrf = None
     if 'vrf_context' in config_dict:
         vrf = config_dict['vrf_context']
 
     # eqivalent of the C foo ? 'a' : 'b' statement
     ospfv3 = vrf and config_dict['vrf']['name'][vrf]['protocols']['ospfv3'] or config_dict['ospfv3']
     ospfv3['policy'] = config_dict['policy']
 
     verify_common_route_maps(ospfv3)
 
     # As we can have a default-information route-map, we need to validate it!
     route_map_name = dict_search('default_information.originate.route_map', ospfv3)
     if route_map_name: verify_route_map(route_map_name, ospfv3)
 
     if 'area' in ospfv3:
         for area, area_config in ospfv3['area'].items():
             if 'area_type' in area_config:
                 if len(area_config['area_type']) > 1:
                     raise ConfigError(f'Can only configure one area-type for OSPFv3 area "{area}"!')
             if 'range' in area_config:
                 for range, range_config in area_config['range'].items():
                     if {'not_advertise', 'advertise'} <= range_config.keys():
                         raise ConfigError(f'"not-advertise" and "advertise" for "range {range}" cannot be both configured at the same time!')
 
     if 'interface' in ospfv3:
         for interface, interface_config in ospfv3['interface'].items():
             verify_interface_exists(ospfv3, interface)
             if 'ifmtu' in interface_config:
                 mtu = Interface(interface).get_mtu()
                 if int(interface_config['ifmtu']) > int(mtu):
                     raise ConfigError(f'OSPFv3 ifmtu can not exceed physical MTU of "{mtu}"')
 
             # If interface specific options are set, we must ensure that the
             # interface is bound to our requesting VRF. Due to the VyOS
             # priorities the interface is bound to the VRF after creation of
             # the VRF itself, and before any routing protocol is configured.
             if vrf:
                 tmp = get_interface_config(interface)
                 if 'master' not in tmp or tmp['master'] != vrf:
                     raise ConfigError(f'Interface "{interface}" is not a member of VRF "{vrf}"!')
 
     return None
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_pim.py b/src/conf_mode/protocols_pim.py
index c40b9d86a..477895b0b 100755
--- a/src/conf_mode/protocols_pim.py
+++ b/src/conf_mode/protocols_pim.py
@@ -1,118 +1,119 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2020-2024 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 ipaddress import IPv4Address
 from ipaddress import IPv4Network
 from signal import SIGTERM
 from sys import exit
 
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import verify_interface_exists
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.frrender import FRRender
 from vyos.frrender import pim_daemon
+from vyos.utils.process import is_systemd_service_running
 from vyos.utils.process import process_named_running
 from vyos.utils.process import call
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'pim'):
         return None
 
     pim = config_dict['pim']
 
     if 'deleted' in pim:
         return None
 
     if 'igmp_proxy_enabled' in pim:
         raise ConfigError('IGMP proxy and PIM cannot be configured at the same time!')
 
     if 'interface' not in pim:
         raise ConfigError('PIM require defined interfaces!')
 
     RESERVED_MC_NET = '224.0.0.0/24'
     for interface, interface_config in pim['interface'].items():
         verify_interface_exists(pim, interface)
 
         # Check join group in reserved net
         if 'igmp' in interface_config and 'join' in interface_config['igmp']:
             for join_addr in interface_config['igmp']['join']:
                 if IPv4Address(join_addr) in IPv4Network(RESERVED_MC_NET):
                     raise ConfigError(f'Groups within {RESERVED_MC_NET} are reserved and cannot be joined!')
 
     if 'rp' in pim:
         if 'address' not in pim['rp']:
             raise ConfigError('PIM rendezvous point needs to be defined!')
 
         # Check unique multicast groups
         unique = []
         pim_base_error = 'PIM rendezvous point group'
         for address, address_config in pim['rp']['address'].items():
             if 'group' not in address_config:
                 raise ConfigError(f'{pim_base_error} should be defined for "{address}"!')
 
             # Check if it is a multicast group
             for gr_addr in address_config['group']:
                 if not IPv4Network(gr_addr).is_multicast:
                     raise ConfigError(f'{pim_base_error} "{gr_addr}" is not a multicast group!')
                 if gr_addr in unique:
                     raise ConfigError(f'{pim_base_error} must be unique!')
                 unique.append(gr_addr)
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'pim'):
         return None
 
     pim_pid = process_named_running(pim_daemon)
     pim = config_dict['pim']
     if 'deleted' in pim:
         os.kill(int(pim_pid), SIGTERM)
         return None
 
     if not pim_pid:
         call('/usr/lib/frr/pimd -d -F traditional --daemon -A 127.0.0.1')
 
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_pim6.py b/src/conf_mode/protocols_pim6.py
index ff8fa38c3..3a9b876cc 100755
--- a/src/conf_mode/protocols_pim6.py
+++ b/src/conf_mode/protocols_pim6.py
@@ -1,95 +1,96 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2023 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 ipaddress import IPv6Address
 from ipaddress import IPv6Network
 from sys import exit
 
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.configverify import verify_interface_exists
+from vyos.utils.process import is_systemd_service_running
 from vyos.frrender import FRRender
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'pim6'):
         return None
 
     pim6 = config_dict['pim6']
     if 'deleted' in pim6:
         return None
 
     for interface, interface_config in pim6.get('interface', {}).items():
         verify_interface_exists(pim6, interface)
         if 'mld' in interface_config:
             mld = interface_config['mld']
             for group in mld.get('join', {}).keys():
                 # Validate multicast group address
                 if not IPv6Address(group).is_multicast:
                     raise ConfigError(f"{group} is not a multicast group")
 
     if 'rp' in pim6:
         if 'address' not in pim6['rp']:
             raise ConfigError('PIM6 rendezvous point needs to be defined!')
 
         # Check unique multicast groups
         unique = []
         pim_base_error = 'PIM6 rendezvous point group'
 
         if {'address', 'prefix-list6'} <= set(pim6['rp']):
             raise ConfigError(f'{pim_base_error} supports either address or a prefix-list!')
 
         for address, address_config in pim6['rp']['address'].items():
             if 'group' not in address_config:
                 raise ConfigError(f'{pim_base_error} should be defined for "{address}"!')
 
             # Check if it is a multicast group
             for gr_addr in address_config['group']:
                 if not IPv6Network(gr_addr).is_multicast:
                     raise ConfigError(f'{pim_base_error} "{gr_addr}" is not a multicast group!')
                 if gr_addr in unique:
                     raise ConfigError(f'{pim_base_error} must be unique!')
                 unique.append(gr_addr)
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_rip.py b/src/conf_mode/protocols_rip.py
index 38108c9f5..39743f965 100755
--- a/src/conf_mode/protocols_rip.py
+++ b/src/conf_mode/protocols_rip.py
@@ -1,88 +1,89 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2021-2024 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.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.configverify import verify_common_route_maps
 from vyos.configverify import verify_access_list
 from vyos.configverify import verify_prefix_list
 from vyos.frrender import FRRender
 from vyos.utils.dict import dict_search
+from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'rip'):
         return None
 
     rip = config_dict['rip']
     rip['policy'] = config_dict['policy']
 
     verify_common_route_maps(rip)
 
     acl_in = dict_search('distribute_list.access_list.in', rip)
     if acl_in: verify_access_list(acl_in, rip)
 
     acl_out = dict_search('distribute_list.access_list.out', rip)
     if acl_out: verify_access_list(acl_out, rip)
 
     prefix_list_in = dict_search('distribute_list.prefix-list.in', rip)
     if prefix_list_in: verify_prefix_list(prefix_list_in, rip)
 
     prefix_list_out = dict_search('distribute_list.prefix_list.out', rip)
     if prefix_list_out: verify_prefix_list(prefix_list_out, rip)
 
     if 'interface' in rip:
         for interface, interface_options in rip['interface'].items():
             if 'authentication' in interface_options:
                 if {'md5', 'plaintext_password'} <= set(interface_options['authentication']):
                     raise ConfigError('Can not use both md5 and plaintext-password at the same time!')
             if 'split_horizon' in interface_options:
                 if {'disable', 'poison_reverse'} <= set(interface_options['split_horizon']):
                     raise ConfigError(f'You can not have "split-horizon poison-reverse" enabled ' \
                                       f'with "split-horizon disable" for "{interface}"!')
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_ripng.py b/src/conf_mode/protocols_ripng.py
index 46f3ce014..14f038444 100755
--- a/src/conf_mode/protocols_ripng.py
+++ b/src/conf_mode/protocols_ripng.py
@@ -1,88 +1,89 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2021-2024 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.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.configverify import verify_common_route_maps
 from vyos.configverify import verify_access_list
 from vyos.configverify import verify_prefix_list
 from vyos.frrender import FRRender
 from vyos.utils.dict import dict_search
+from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'ripng'):
         return None
 
     ripng = config_dict['ripng']
     ripng['policy'] = config_dict['policy']
 
     verify_common_route_maps(ripng)
 
     acl_in = dict_search('distribute_list.access_list.in', ripng)
     if acl_in: verify_access_list(acl_in, ripng, version='6')
 
     acl_out = dict_search('distribute_list.access_list.out', ripng)
     if acl_out: verify_access_list(acl_out, ripng, version='6')
 
     prefix_list_in = dict_search('distribute_list.prefix_list.in', ripng)
     if prefix_list_in: verify_prefix_list(prefix_list_in, ripng, version='6')
 
     prefix_list_out = dict_search('distribute_list.prefix_list.out', ripng)
     if prefix_list_out: verify_prefix_list(prefix_list_out, ripng, version='6')
 
     if 'interface' in ripng:
         for interface, interface_options in ripng['interface'].items():
             if 'authentication' in interface_options:
                 if {'md5', 'plaintext_password'} <= set(interface_options['authentication']):
                     raise ConfigError('Can not use both md5 and plaintext-password at the same time!')
             if 'split_horizon' in interface_options:
                 if {'disable', 'poison_reverse'} <= set(interface_options['split_horizon']):
                     raise ConfigError(f'You can not have "split-horizon poison-reverse" enabled ' \
                                       f'with "split-horizon disable" for "{interface}"!')
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_rpki.py b/src/conf_mode/protocols_rpki.py
index 4aefbe36c..5ad656586 100755
--- a/src/conf_mode/protocols_rpki.py
+++ b/src/conf_mode/protocols_rpki.py
@@ -1,115 +1,115 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2021-2024 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 sys import exit
 
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.frrender import FRRender
 from vyos.pki import wrap_openssh_public_key
 from vyos.pki import wrap_openssh_private_key
 from vyos.utils.dict import dict_search_args
-from vyos.utils.process import is_systemd_service_running
 from vyos.utils.file import write_file
+from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 rpki_ssh_key_base = '/run/frr/id_rpki'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'rpki'):
         return None
 
     rpki = config_dict['rpki']
 
     if 'cache' in rpki:
         preferences = []
         for peer, peer_config in rpki['cache'].items():
             for mandatory in ['port', 'preference']:
                 if mandatory not in peer_config:
                     raise ConfigError(f'RPKI cache "{peer}" {mandatory} must be defined!')
 
             if 'preference' in peer_config:
                 preference = peer_config['preference']
                 if preference in preferences:
                     raise ConfigError(f'RPKI cache with preference {preference} already configured!')
                 preferences.append(preference)
 
             if 'ssh' in peer_config:
                 if 'username' not in peer_config['ssh']:
                     raise ConfigError('RPKI+SSH requires username to be defined!')
 
                 if 'key' not in peer_config['ssh'] or 'openssh' not in rpki['pki']:
                     raise ConfigError('RPKI+SSH requires key to be defined!')
 
                 if peer_config['ssh']['key'] not in rpki['pki']['openssh']:
                     raise ConfigError('RPKI+SSH key not found on PKI subsystem!')
 
     return None
 
 def generate(config_dict):
     for key in glob(f'{rpki_ssh_key_base}*'):
         os.unlink(key)
 
     if not has_frr_protocol_in_dict(config_dict, 'rpki'):
         return None
 
     rpki = config_dict['rpki']
 
     if 'cache' in rpki:
         for cache, cache_config in rpki['cache'].items():
             if 'ssh' in cache_config:
                 key_name = cache_config['ssh']['key']
                 public_key_data = dict_search_args(rpki['pki'], 'openssh', key_name, 'public', 'key')
                 public_key_type = dict_search_args(rpki['pki'], 'openssh', key_name, 'public', 'type')
                 private_key_data = dict_search_args(rpki['pki'], 'openssh', key_name, 'private', 'key')
 
                 cache_config['ssh']['public_key_file'] = f'{rpki_ssh_key_base}_{cache}.pub'
                 cache_config['ssh']['private_key_file'] = f'{rpki_ssh_key_base}_{cache}'
 
                 write_file(cache_config['ssh']['public_key_file'], wrap_openssh_public_key(public_key_data, public_key_type))
                 write_file(cache_config['ssh']['private_key_file'], wrap_openssh_private_key(private_key_data))
 
     if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
     if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_segment-routing.py b/src/conf_mode/protocols_segment-routing.py
index cc42e5ac7..99cf87556 100755
--- a/src/conf_mode/protocols_segment-routing.py
+++ b/src/conf_mode/protocols_segment-routing.py
@@ -1,103 +1,104 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2023-2024 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.configdict import get_frrender_dict
 from vyos.configdict import list_diff
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.frrender import FRRender
 from vyos.ifconfig import Section
 from vyos.utils.dict import dict_search
+from vyos.utils.process import is_systemd_service_running
 from vyos.utils.system import sysctl_write
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'segment_routing'):
         return None
 
     sr = config_dict['segment_routing']
 
     if 'srv6' in sr:
         srv6_enable = False
         if 'interface' in sr:
             for interface, interface_config in sr['interface'].items():
                 if 'srv6' in interface_config:
                     srv6_enable = True
                     break
         if not srv6_enable:
             raise ConfigError('SRv6 should be enabled on at least one interface!')
     return None
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'segment_routing'):
         return None
 
     sr = config_dict['segment_routing']
 
     current_interfaces = Section.interfaces()
     sr_interfaces = list(sr.get('interface', {}).keys())
 
     for interface in list_diff(current_interfaces, sr_interfaces):
         # Disable processing of IPv6-SR packets
         sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '0')
 
     for interface, interface_config in sr.get('interface', {}).items():
         # Accept or drop SR-enabled IPv6 packets on this interface
         if 'srv6' in interface_config:
             sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '1')
             # Define HMAC policy for ingress SR-enabled packets on this interface
             # It's a redundant check as HMAC has a default value - but better safe
             # then sorry
             tmp = dict_search('srv6.hmac', interface_config)
             if tmp == 'accept':
                 sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '0')
             elif tmp == 'drop':
                 sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '1')
             elif tmp == 'ignore':
                 sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '-1')
         else:
             sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '0')
 
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_static.py b/src/conf_mode/protocols_static.py
index 29fa530f4..9d02db6dd 100755
--- a/src/conf_mode/protocols_static.py
+++ b/src/conf_mode/protocols_static.py
@@ -1,114 +1,115 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2021-2024 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 ipaddress import IPv4Network
 from sys import exit
 from sys import argv
 
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.configverify import verify_common_route_maps
 from vyos.configverify import verify_vrf
 from vyos.frrender import FRRender
+from vyos.utils.process import is_systemd_service_running
 from vyos.template import render
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 config_file = '/etc/iproute2/rt_tables.d/vyos-static.conf'
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     return get_frrender_dict(conf, argv)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'static'):
         return None
 
     vrf = None
     if 'vrf_context' in config_dict:
         vrf = config_dict['vrf_context']
 
     # eqivalent of the C foo ? 'a' : 'b' statement
     static = vrf and config_dict['vrf']['name'][vrf]['protocols']['static'] or config_dict['static']
     static['policy'] = config_dict['policy']
 
     verify_common_route_maps(static)
 
     for route in ['route', 'route6']:
         # if there is no route(6) key in the dictionary we can immediately
         # bail out early
         if route not in static:
             continue
 
         # When leaking routes to other VRFs we must ensure that the destination
         # VRF exists
         for prefix, prefix_options in static[route].items():
             # both the interface and next-hop CLI node can have a VRF subnode,
             # thus we check this using a for loop
             for type in ['interface', 'next_hop']:
                 if type in prefix_options:
                     for interface, interface_config in prefix_options[type].items():
                         verify_vrf(interface_config)
 
             if {'blackhole', 'reject'} <= set(prefix_options):
                 raise ConfigError(f'Can not use both blackhole and reject for '\
                                   f'prefix "{prefix}"!')
 
     if 'multicast' in static and 'route' in static['multicast']:
         for prefix, prefix_options in static['multicast']['route'].items():
             if not IPv4Network(prefix).is_multicast:
                 raise ConfigError(f'{prefix} is not a multicast network!')
 
     return None
 
 def generate(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'static'):
         return None
 
     vrf = None
     if 'vrf_context' in config_dict:
         vrf = config_dict['vrf_context']
 
     # eqivalent of the C foo ? 'a' : 'b' statement
     static = vrf and config_dict['vrf']['name'][vrf]['protocols']['static'] or config_dict['static']
 
     # Put routing table names in /etc/iproute2/rt_tables
     render(config_file, 'iproute2/static.conf.j2', static)
 
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
     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_ip.py b/src/conf_mode/system_ip.py
index 374e6e611..86843eb78 100755
--- a/src/conf_mode/system_ip.py
+++ b/src/conf_mode/system_ip.py
@@ -1,126 +1,127 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-2024 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.configdep import set_dependents
 from vyos.configdep import call_dependents
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.configverify import verify_route_map
 from vyos.frrender import FRRender
 from vyos.utils.dict import dict_search
 from vyos.utils.process import is_systemd_service_active
+from vyos.utils.process import is_systemd_service_running
 from vyos.utils.system import sysctl_write
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     # If IPv4 ARP table size is set here and also manually in sysctl, the more
     # fine grained value from sysctl must win
     set_dependents('sysctl', conf)
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'ip'):
         return None
 
     opt = config_dict['ip']
     opt['policy'] = config_dict['policy']
 
     if 'protocol' in opt:
         for protocol, protocol_options in opt['protocol'].items():
             if 'route_map' in protocol_options:
                 verify_route_map(protocol_options['route_map'], opt)
     return
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'ip'):
 
         return None
     opt = config_dict['ip']
 
     # Apply ARP threshold values
     # table_size has a default value - thus the key always exists
     size = int(dict_search('arp.table_size', opt))
     # Amount upon reaching which the records begin to be cleared immediately
     sysctl_write('net.ipv4.neigh.default.gc_thresh3', size)
     # Amount after which the records begin to be cleaned after 5 seconds
     sysctl_write('net.ipv4.neigh.default.gc_thresh2', size // 2)
     # Minimum number of stored records is indicated which is not cleared
     sysctl_write('net.ipv4.neigh.default.gc_thresh1', size // 8)
 
     # configure multipath
     tmp = dict_search('multipath.ignore_unreachable_nexthops', opt)
     value = '1' if (tmp != None) else '0'
     sysctl_write('net.ipv4.fib_multipath_use_neigh', value)
 
     tmp = dict_search('multipath.layer4_hashing', opt)
     value = '1' if (tmp != None) else '0'
     sysctl_write('net.ipv4.fib_multipath_hash_policy', value)
 
     # configure TCP options (defaults as of Linux 6.4)
     tmp = dict_search('tcp.mss.probing', opt)
     if tmp is None:
         value = 0
     elif tmp == 'on-icmp-black-hole':
         value = 1
     elif tmp == 'force':
         value = 2
     else:
         # Shouldn't happen
         raise ValueError("TCP MSS probing is neither 'on-icmp-black-hole' nor 'force'!")
     sysctl_write('net.ipv4.tcp_mtu_probing', value)
 
     tmp = dict_search('tcp.mss.base', opt)
     value = '1024' if (tmp is None) else tmp
     sysctl_write('net.ipv4.tcp_base_mss', value)
 
     tmp = dict_search('tcp.mss.floor', opt)
     value = '48' if (tmp is None) else tmp
     sysctl_write('net.ipv4.tcp_mtu_probe_floor', value)
 
     # During startup of vyos-router that brings up FRR, the service is not yet
     # running when this script is called first. Skip this part and wait for initial
     # commit of the configuration to trigger this statement
     if is_systemd_service_active('frr.service'):
-        if config_dict and 'frrender_cls' not in config_dict:
+        if config_dict and not is_systemd_service_running('vyos-configd.service'):
             FRRender().apply()
 
     call_dependents()
     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_ipv6.py b/src/conf_mode/system_ipv6.py
index 02c9a8201..593b8f7f3 100755
--- a/src/conf_mode/system_ipv6.py
+++ b/src/conf_mode/system_ipv6.py
@@ -1,110 +1,111 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-2024 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.configdep import set_dependents
 from vyos.configdep import call_dependents
 from vyos.configdict import get_frrender_dict
 from vyos.configverify import has_frr_protocol_in_dict
 from vyos.configverify import verify_route_map
 from vyos.frrender import FRRender
 from vyos.utils.dict import dict_search
 from vyos.utils.file import write_file
 from vyos.utils.process import is_systemd_service_active
+from vyos.utils.process import is_systemd_service_running
 from vyos.utils.system import sysctl_write
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     # If IPv6 neighbor table size is set here and also manually in sysctl, the more
     # fine grained value from sysctl must win
     set_dependents('sysctl', conf)
     return get_frrender_dict(conf)
 
 def verify(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'ipv6'):
         return None
 
     opt = config_dict['ipv6']
     opt['policy'] = config_dict['policy']
 
     if 'protocol' in opt:
         for protocol, protocol_options in opt['protocol'].items():
             if 'route_map' in protocol_options:
                 verify_route_map(protocol_options['route_map'], opt)
     return
 
 def generate(config_dict):
-    if config_dict and 'frrender_cls' not in config_dict:
+    if config_dict and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(config_dict)
     return None
 
 def apply(config_dict):
     if not has_frr_protocol_in_dict(config_dict, 'ipv6'):
         return None
     opt = config_dict['ipv6']
 
     # configure multipath
     tmp = dict_search('multipath.layer4_hashing', opt)
     value = '1' if (tmp != None) else '0'
     sysctl_write('net.ipv6.fib_multipath_hash_policy', value)
 
     # Apply ND threshold values
     # table_size has a default value - thus the key always exists
     size = int(dict_search('neighbor.table_size', opt))
     # Amount upon reaching which the records begin to be cleared immediately
     sysctl_write('net.ipv6.neigh.default.gc_thresh3', size)
     # Amount after which the records begin to be cleaned after 5 seconds
     sysctl_write('net.ipv6.neigh.default.gc_thresh2', size // 2)
     # Minimum number of stored records is indicated which is not cleared
     sysctl_write('net.ipv6.neigh.default.gc_thresh1', size // 8)
 
     # configure IPv6 strict-dad
     tmp = dict_search('strict_dad', opt)
     value = '2' if (tmp != None) else '1'
     for root, dirs, files in os.walk('/proc/sys/net/ipv6/conf'):
         for name in files:
             if name == 'accept_dad':
                 write_file(os.path.join(root, name), value)
 
     # During startup of vyos-router that brings up FRR, the service is not yet
     # running when this script is called first. Skip this part and wait for initial
     # commit of the configuration to trigger this statement
     if is_systemd_service_active('frr.service'):
-        if config_dict and 'frrender_cls' not in config_dict:
+        if config_dict and not is_systemd_service_running('vyos-configd.service'):
             FRRender().apply()
 
     call_dependents()
     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/vrf.py b/src/conf_mode/vrf.py
index 1b19c55d2..6533f493f 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -1,357 +1,358 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2020-2024 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 jmespath import search
 from json import loads
 
 from vyos.config import Config
 from vyos.configdict import get_frrender_dict
 from vyos.configdict import node_changed
 from vyos.configverify import verify_route_map
 from vyos.firewall import conntrack_required
 from vyos.frrender import FRRender
 from vyos.ifconfig import Interface
 from vyos.template import render
 from vyos.utils.dict import dict_search
 from vyos.utils.network import get_vrf_tableid
 from vyos.utils.network import get_vrf_members
 from vyos.utils.network import interface_exists
 from vyos.utils.process import call
 from vyos.utils.process import cmd
+from vyos.utils.process import is_systemd_service_running
 from vyos.utils.process import popen
 from vyos.utils.system import sysctl_write
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 config_file = '/etc/iproute2/rt_tables.d/vyos-vrf.conf'
 k_mod = ['vrf']
 
 nftables_table = 'inet vrf_zones'
 nftables_rules = {
     'vrf_zones_ct_in': 'counter ct original zone set iifname map @ct_iface_map',
     'vrf_zones_ct_out': 'counter ct original zone set oifname map @ct_iface_map'
 }
 
 def has_rule(af : str, priority : int, table : str=None):
     """
     Check if a given ip rule exists
     $ ip --json -4 rule show
     [{'l3mdev': None, 'priority': 1000, 'src': 'all'},
     {'action': 'unreachable', 'l3mdev': None, 'priority': 2000, 'src': 'all'},
     {'priority': 32765, 'src': 'all', 'table': 'local'},
     {'priority': 32766, 'src': 'all', 'table': 'main'},
     {'priority': 32767, 'src': 'all', 'table': 'default'}]
     """
     if af not in ['-4', '-6']:
         raise ValueError()
     command = f'ip --detail --json {af} rule show'
     for tmp in loads(cmd(command)):
         if 'priority' in tmp and 'table' in tmp:
             if tmp['priority'] == priority and tmp['table'] == table:
                 return True
         elif 'priority' in tmp and table in tmp:
             # l3mdev table has a different layout
             if tmp['priority'] == priority:
                 return True
     return False
 
 def is_nft_vrf_zone_rule_setup() -> bool:
     """
     Check if an nftables connection tracking rule already exists
     """
     tmp = loads(cmd('sudo nft -j list table inet vrf_zones'))
     num_rules = len(search("nftables[].rule[].chain", tmp))
     return bool(num_rules)
 
 def vrf_interfaces(c, match):
     matched = []
     old_level = c.get_level()
     c.set_level(['interfaces'])
     section = c.get_config_dict([], get_first_key=True)
     for type in section:
         interfaces = section[type]
         for name in interfaces:
             interface = interfaces[name]
             if 'vrf' in interface:
                 v = interface.get('vrf', '')
                 if v == match:
                     matched.append(name)
 
     c.set_level(old_level)
     return matched
 
 def vrf_routing(c, match):
     matched = []
     old_level = c.get_level()
     c.set_level(['protocols', 'vrf'])
     if match in c.list_nodes([]):
         matched.append(match)
 
     c.set_level(old_level)
     return matched
 
 def get_config(config=None):
     if config:
         conf = config
     else:
         conf = Config()
 
     base = ['vrf']
     vrf = conf.get_config_dict(base, key_mangling=('-', '_'),
                                no_tag_node_value_mangle=True, get_first_key=True)
 
     # determine which VRF has been removed
     for name in node_changed(conf, base + ['name']):
         if 'vrf_remove' not in vrf:
             vrf.update({'vrf_remove' : {}})
 
         vrf['vrf_remove'][name] = {}
         # get VRF bound interfaces
         interfaces = vrf_interfaces(conf, name)
         if interfaces: vrf['vrf_remove'][name]['interface'] = interfaces
         # get VRF bound routing instances
         routes = vrf_routing(conf, name)
         if routes: vrf['vrf_remove'][name]['route'] = routes
 
     if 'name' in vrf:
         vrf['conntrack'] = conntrack_required(conf)
 
     # We need to merge the FRR rendering dict into the VRF dict
     # this is required to get the route-map information to FRR
     vrf.update({'frr_dict' : get_frrender_dict(conf)})
     return vrf
 
 def verify(vrf):
     # ensure VRF is not assigned to any interface
     if 'vrf_remove' in vrf:
         for name, config in vrf['vrf_remove'].items():
             if 'interface' in config:
                 raise ConfigError(f'Can not remove VRF "{name}", it still has '\
                                   f'member interfaces!')
             if 'route' in config:
                 raise ConfigError(f'Can not remove VRF "{name}", it still has '\
                                   f'static routes installed!')
 
     if 'name' in vrf:
         reserved_names = ["add", "all", "broadcast", "default", "delete", "dev",
                           "get", "inet", "mtu", "link", "type", "vrf"]
         table_ids = []
         vnis = []
         for name, vrf_config in vrf['name'].items():
             # Reserved VRF names
             if name in reserved_names:
                 raise ConfigError(f'VRF name "{name}" is reserved and connot be used!')
 
             # table id is mandatory
             if 'table' not in vrf_config:
                 raise ConfigError(f'VRF "{name}" table id is mandatory!')
 
             # routing table id can't be changed - OS restriction
             if interface_exists(name):
                 tmp = get_vrf_tableid(name)
                 if tmp and tmp != int(vrf_config['table']):
                     raise ConfigError(f'VRF "{name}" table id modification not possible!')
 
             # VRF routing table ID must be unique on the system
             if 'table' in vrf_config and vrf_config['table'] in table_ids:
                 raise ConfigError(f'VRF "{name}" table id is not unique!')
             table_ids.append(vrf_config['table'])
 
             # VRF VNIs must be unique on the system
             if 'vni' in vrf_config:
                 vni = vrf_config['vni']
                 if vni in vnis:
                     raise ConfigError(f'VRF "{name}" VNI "{vni}" is not unique!')
                 vnis.append(vni)
 
             tmp = dict_search('ip.protocol', vrf_config)
             if tmp != None:
                 for protocol, protocol_options in tmp.items():
                     if 'route_map' in protocol_options:
                         verify_route_map(protocol_options['route_map'], vrf['frr_dict'])
 
             tmp = dict_search('ipv6.protocol', vrf_config)
             if tmp != None:
                 for protocol, protocol_options in tmp.items():
                     if 'route_map' in protocol_options:
                         verify_route_map(protocol_options['route_map'], vrf['frr_dict'])
 
     return None
 
 
 def generate(vrf):
     # Render iproute2 VR helper names
     render(config_file, 'iproute2/vrf.conf.j2', vrf)
 
-    if 'frr_dict' in vrf and 'frrender_cls' not in vrf['frr_dict']:
+    if 'frr_dict' in vrf and not is_systemd_service_running('vyos-configd.service'):
         FRRender().generate(vrf['frr_dict'])
 
     return None
 
 def apply(vrf):
     # Documentation
     #
     # - https://github.com/torvalds/linux/blob/master/Documentation/networking/vrf.txt
     # - https://github.com/Mellanox/mlxsw/wiki/Virtual-Routing-and-Forwarding-(VRF)
     # - https://github.com/Mellanox/mlxsw/wiki/L3-Tunneling
     # - https://netdevconf.info/1.1/proceedings/slides/ahern-vrf-tutorial.pdf
     # - https://netdevconf.info/1.2/slides/oct6/02_ahern_what_is_l3mdev_slides.pdf
 
     # set the default VRF global behaviour
     bind_all = '0'
     if 'bind_to_all' in vrf:
         bind_all = '1'
     sysctl_write('net.ipv4.tcp_l3mdev_accept', bind_all)
     sysctl_write('net.ipv4.udp_l3mdev_accept', bind_all)
 
     for tmp in (dict_search('vrf_remove', vrf) or []):
         if interface_exists(tmp):
             # T5492: deleting a VRF instance may leafe processes running
             # (e.g. dhclient) as there is a depedency ordering issue in the CLI.
             # We need to ensure that we stop the dhclient processes first so
             # a proper DHCLP RELEASE message is sent
             for interface in get_vrf_members(tmp):
                 vrf_iface = Interface(interface)
                 vrf_iface.set_dhcp(False)
                 vrf_iface.set_dhcpv6(False)
 
             # Remove nftables conntrack zone map item
             nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ "{tmp}" }}'
             # Check if deleting is possible first to avoid raising errors
             _, err = popen(f'nft --check {nft_del_element}')
             if not err:
                 # Remove map element
                 cmd(f'nft {nft_del_element}')
 
             # Delete the VRF Kernel interface
             call(f'ip link delete dev {tmp}')
 
     if 'name' in vrf:
         # Linux routing uses rules to find tables - routing targets are then
         # looked up in those tables. If the lookup got a matching route, the
         # process ends.
         #
         # TL;DR; first table with a matching entry wins!
         #
         # You can see your routing table lookup rules using "ip rule", sadly the
         # local lookup is hit before any VRF lookup. Pinging an addresses from the
         # VRF will usually find a hit in the local table, and never reach the VRF
         # routing table - this is usually not what you want. Thus we will
         # re-arrange the tables and move the local lookup further down once VRFs
         # are enabled.
         #
         # Thanks to https://stbuehler.de/blog/article/2020/02/29/using_vrf__virtual_routing_and_forwarding__on_linux.html
 
         for afi in ['-4', '-6']:
             # move lookup local to pref 32765 (from 0)
             if not has_rule(afi, 32765, 'local'):
                 call(f'ip {afi} rule add pref 32765 table local')
             if has_rule(afi, 0, 'local'):
                 call(f'ip {afi} rule del pref 0')
             # make sure that in VRFs after failed lookup in the VRF specific table
             # nothing else is reached
             if not has_rule(afi, 1000, 'l3mdev'):
                 # this should be added by the kernel when a VRF is created
                 # add it here for completeness
                 call(f'ip {afi} rule add pref 1000 l3mdev protocol kernel')
 
             # add another rule with an unreachable target which only triggers in VRF context
             # if a route could not be reached
             if not has_rule(afi, 2000, 'l3mdev'):
                 call(f'ip {afi} rule add pref 2000 l3mdev unreachable')
 
         nft_vrf_zone_rule_setup = False
         for name, config in vrf['name'].items():
             table = config['table']
             if not interface_exists(name):
                 # For each VRF apart from your default context create a VRF
                 # interface with a separate routing table
                 call(f'ip link add {name} type vrf table {table}')
 
             # set VRF description for e.g. SNMP monitoring
             vrf_if = Interface(name)
             # We also should add proper loopback IP addresses to the newly added
             # VRF for services bound to the loopback address (SNMP, NTP)
             vrf_if.add_addr('127.0.0.1/8')
             vrf_if.add_addr('::1/128')
             # add VRF description if available
             vrf_if.set_alias(config.get('description', ''))
 
             # Enable/Disable IPv4 forwarding
             tmp = dict_search('ip.disable_forwarding', config)
             value = '0' if (tmp != None) else '1'
             vrf_if.set_ipv4_forwarding(value)
             # Enable/Disable IPv6 forwarding
             tmp = dict_search('ipv6.disable_forwarding', config)
             value = '0' if (tmp != None) else '1'
             vrf_if.set_ipv6_forwarding(value)
 
             # 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'
             vrf_if.set_admin_state(state)
             # Add nftables conntrack zone map item
             nft_add_element = f'add element inet vrf_zones ct_iface_map {{ "{name}" : {table} }}'
             cmd(f'nft {nft_add_element}')
 
         # Only call into nftables as long as there is nothing setup to avoid wasting
         # CPU time and thus lenghten the commit process
         if not nft_vrf_zone_rule_setup:
             nft_vrf_zone_rule_setup = is_nft_vrf_zone_rule_setup()
         # Install nftables conntrack rules only once
         if vrf['conntrack'] and not nft_vrf_zone_rule_setup:
             for chain, rule in nftables_rules.items():
                 cmd(f'nft add rule inet vrf_zones {chain} {rule}')
 
     if 'name' not in vrf or not vrf['conntrack']:
         for chain, rule in nftables_rules.items():
             cmd(f'nft flush chain inet vrf_zones {chain}')
 
     # Return default ip rule values
     if 'name' not in vrf:
         for afi in ['-4', '-6']:
             # move lookup local to pref 0 (from 32765)
             if not has_rule(afi, 0, 'local'):
                 call(f'ip {afi} rule add pref 0 from all lookup local')
             if has_rule(afi, 32765, 'local'):
                 call(f'ip {afi} rule del pref 32765 table local')
 
             if has_rule(afi, 1000, 'l3mdev'):
                 call(f'ip {afi} rule del pref 1000 l3mdev protocol kernel')
             if has_rule(afi, 2000, 'l3mdev'):
                 call(f'ip {afi} rule del pref 2000 l3mdev unreachable')
 
-    if 'frr_dict' in vrf and 'frrender_cls' not in vrf['frr_dict']:
+    if 'frr_dict' in vrf and not is_systemd_service_running('vyos-configd.service'):
         FRRender().apply()
 
     return None
 
 if __name__ == '__main__':
     try:
         c = get_config()
         verify(c)
         generate(c)
         apply(c)
     except ConfigError as e:
         print(e)
         exit(1)