From 51fdb57668073d16f5f4795ab9ebdee5b3b853c2 Mon Sep 17 00:00:00 2001
From: sever-sever <v.gletenko@vyos.io>
Date: Fri, 23 Oct 2020 15:26:41 +0000
Subject: isis: T1316: October steps

---
 src/conf_mode/protocols_isis.py | 137 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 137 insertions(+)
 create mode 100755 src/conf_mode/protocols_isis.py

(limited to 'src/conf_mode')

diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
new file mode 100755
index 000000000..4ee6b31f6
--- /dev/null
+++ b/src/conf_mode/protocols_isis.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2017-2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+from sys import exit
+
+from vyos.config import Config
+from vyos import ConfigError
+from vyos.util import call
+from vyos.template import render
+from vyos.template import render_to_string
+from vyos import frr
+from vyos import airbag
+from pprint import pprint
+airbag.enable()
+
+config_file = r'/tmp/isis.frr'
+
+default_config_data = {
+    'interface':  ''
+}
+
+def get_config():
+    conf = Config()
+    base = ['protocols', 'isis']
+    isis = conf.get_config_dict(base, key_mangling=('-', '_'))
+    if not conf.exists(base):
+        isis = {}
+
+    pprint(isis)
+    return isis
+
+def verify(isis):
+    # bail out early - looks like removal from running config
+    if not isis:
+        return None
+
+    for proc_id, foo_config in isis['isis'].items():
+
+        # If more then one isis process is defined (Frr only supports one)
+        # http://docs.frrouting.org/en/latest/isisd.html#isis-router
+        if len(isis['isis']) > 1:
+            raise ConfigError('Only one isis process can be definded')
+
+        # If network entity title (net) not defined
+        if not "net" in  foo_config:
+          raise ConfigError('Define net format iso is mandatory in \"isis {} net"!'.format(proc_id))
+
+        # If interface not set
+        if not "interface" in  foo_config:
+          raise ConfigError('Define interface is mandatory in \"isis {} interface"!'.format(proc_id))
+
+        # If md5 and plaintext-password set at the same time
+        if 'area_password' in foo_config:
+            if "md5" in foo_config['area_password'] and "plaintext_password" in foo_config['area_password']:
+                raise ConfigError('Only one password type should be used in \"isis {} area-password"!'.format(proc_id))
+
+        # If one param from deley set, but not set others
+        if 'spf_delay_ietf' in foo_config:
+            required_timers = ['holddown', 'init_delay', 'long_delay', 'short_delay', 'time_to_learn']
+            exist_timers = []
+            for elm_timer in required_timers:
+                if elm_timer in foo_config['spf_delay_ietf']:
+                    exist_timers.append(elm_timer)
+
+            exist_timers = set(required_timers).difference(set(exist_timers))
+            if len(exist_timers) > 0:
+                raise ConfigError('All types of delay must be specified: ' + ', '.join(exist_timers).replace('_', '-'))
+
+        # If Redistribute set, but level don't set
+        if 'redistribute' in foo_config:
+            proc_level = foo_config.get('level','').replace('-','_')
+            for proto, proto_config in foo_config.get('redistribute', {}).get('ipv4', {}).items():
+                if 'level_1' not in proto_config and 'level_2' not in proto_config:
+                    raise ConfigError('Redistribute level-1 or level-2 should be specified in \"protocols isis {} redistribute ipv4 {}\"'.format(proc_id, proto))
+            for redistribute_level in proto_config.keys():
+                if proc_level and proc_level != 'level_1_2' and proc_level != redistribute_level:
+                    raise ConfigError('\"protocols isis {0} redistribute ipv4 {2} {3}\" cannot be used with \"protocols isis {0} level {1}\"'.format(proc_id, proc_level, proto, redistribute_level))
+
+    return None
+
+def generate(isis):
+    if not isis:
+        isis['new_frr_config'] = ''
+        return None
+
+    # render(config) not needed, its only for debug
+    render(config_file, 'frr/isis.frr.tmpl', isis)
+
+    isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl', isis)
+
+    return None
+
+def apply(isis):
+
+    # Save original configration prior to starting any commit actions
+    isis['original_config'] = frr.get_configuration(daemon='isisd')
+    isis['modified_config'] = frr.replace_section(isis['original_config'], isis['new_frr_config'], from_re='router isis .*')
+
+    # Debugging
+    print('')
+    print('--------- DEBUGGING ----------')
+    print(f'Existing config:\n{isis["original_config"]}\n\n')
+    print(f'Replacement config:\n{isis["new_frr_config"]}\n\n')
+    print(f'Modified config:\n{isis["modified_config"]}\n\n')
+
+    # Frr Mark configuration will test for syntax errors and exception out if any syntax errors are detected
+    frr.mark_configuration(isis['modified_config'])
+
+    # Commit the resulting new configuration to frr, this will render an frr.CommitError() Exception on fail
+    frr.reload_configuration(isis['modified_config'], daemon='isisd')
+
+    return None
+
+if __name__ == '__main__':
+    try:
+        c = get_config()
+        verify(c)
+        generate(c)
+        apply(c)
+    except ConfigError as e:
+        print(e)
+        exit(1)
-- 
cgit v1.2.3


From 82193bbcf4a6ae8b661dd30eb627cf1a27593fe9 Mon Sep 17 00:00:00 2001
From: Christian Poessinger <christian@poessinger.com>
Date: Fri, 13 Nov 2020 21:50:51 +0100
Subject: isis: T1316: refactor config retrieval and Jinja2 template

Make the entire template code more human readable by denesting it, as there can
only be one ISIS daemon instance in FRR.
---
 Makefile                         |   1 +
 data/configd-include.json        |   1 +
 data/templates/frr/isis.frr.tmpl | 255 +++++++++++++++++----------------------
 src/conf_mode/protocols_isis.py  |  91 ++++++++------
 4 files changed, 164 insertions(+), 184 deletions(-)

(limited to 'src/conf_mode')

diff --git a/Makefile b/Makefile
index d18676619..fffd1c9c3 100644
--- a/Makefile
+++ b/Makefile
@@ -78,6 +78,7 @@ interface_definitions: $(BUILD_DIR) $(obj)
 	rm -f $(TMPL_DIR)/interfaces/wirelessmodem/node.tag/ipv6/node.def
 	rm -f $(TMPL_DIR)/protocols/node.def
 	rm -rf $(TMPL_DIR)/protocols/nbgp
+	rm -rf $(TMPL_DIR)/protocols/isis
 	rm -f $(TMPL_DIR)/protocols/static/node.def
 	rm -f $(TMPL_DIR)/system/node.def
 	rm -f $(TMPL_DIR)/vpn/node.def
diff --git a/data/configd-include.json b/data/configd-include.json
index da6fb915f..2e44405ee 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -30,6 +30,7 @@
 "nat.py",
 "ntp.py",
 "protocols_igmp.py",
+"protocols_isis.py",
 "protocols_mpls.py",
 "protocols_pim.py",
 "protocols_rip.py",
diff --git a/data/templates/frr/isis.frr.tmpl b/data/templates/frr/isis.frr.tmpl
index 70444d0c3..929f5bdb2 100644
--- a/data/templates/frr/isis.frr.tmpl
+++ b/data/templates/frr/isis.frr.tmpl
@@ -1,158 +1,121 @@
-{%   for dname in isis -%}
 !
-router isis {{dname}}
-{% if isis[dname]['net'] is string -%}
- net {{ isis[dname]['net'] }}
-{% else -%}
-{%   for net_iso in isis[dname]['net'] -%}
- net {{ net_iso }}
-{%   endfor -%}
-{% endif -%}
-{#- end isis net -#}
-{% if 'dynamic_hostname' in isis[dname] -%}
+router isis {{ process }}
+ net {{ net }}
+{% if dynamic_hostname is defined %}
  hostname dynamic
-{% endif -%}
-{% if 'purge_originator' in isis[dname] -%}
+{% endif %}
+{% if purge_originator is defined %}
  purge-originator
-{% endif -%}
-{% if 'set_attached_bit' in isis[dname] -%}
+{% endif %}
+{% if set_attached_bit is defined %}
  set-attached-bit
-{% endif -%}
-{% if 'set_overload_bit' in isis[dname] -%}
+{% endif %}
+{% if set_overload_bit is defined %}
  set-overload-bit
-{% endif -%}
-{% if isis[dname].domain_password is defined -%}
-{%   if isis[dname].domain_password.plaintext_password is defined -%}
- domain-password clear {{ isis[dname].domain_password.plaintext_password }}
-{%   endif -%}
-{% endif -%}
-{#- end isis domain-password -#}
-{% if isis[dname]['lsp_gen_interval'] -%}
- lsp-gen-interval {{ isis[dname]['lsp_gen_interval'] }}
-{% endif -%}
-{% if isis[dname]['lsp_mtu'] -%}
- lsp-mtu {{ isis[dname]['lsp_mtu'] }}
-{% endif -%}
-{% if isis[dname]['lsp_refresh_interval'] -%}
- lsp-mtu {{ isis[dname]['lsp_refresh_interval'] }}
-{% endif -%}
-{% if isis[dname]['max_lsp_lifetime'] -%}
- lsp-mtu {{ isis[dname]['max_lsp_lifetime'] }}
-{% endif -%}
-{% if isis[dname]['spf_interval'] -%}
- spf-interval {{ isis[dname]['spf_interval'] }}
-{% endif -%}
-{% for t in isis[dname]['spf_delay_ietf'] -%}
-{% if t == "holddown" -%}
- spf-delay-ietf holddown {{ isis[dname]['spf_delay_ietf'][t] }}
-{% endif -%}
-{% endfor -%}
-{#- end lsp timers and intervals -#}
-{% if isis[dname]['area_password'] is defined -%}
-{% for password in isis[dname]['area_password'] -%}
-{%   if password == "md5" -%}
- area-password md5 {{ isis[dname]['area_password'][password] }}
-{%   endif -%}
-{%   if password == "plaintext_password" -%}
- area-password clear {{ isis[dname]['area_password'][password] }}
-{%   endif -%}
-{% endfor -%}
-{% endif -%}
-{#- end isis area-password -#}
-{% if 'originate' in isis[dname]['default_information'] -%}
-{% for ipv in isis[dname]['default_information']['originate'] -%}
-{%   for lvl in isis[dname]['default_information']['originate'][ipv] -%}
-{%     if ipv == 'ipv4' -%}
-    default-information originate {{ ipv }} {{ lvl | replace('_', '-') }}
-{%     elif ipv == 'ipv6' -%}
-    default-information originate {{ ipv }} {{ lvl | replace('_', '-') }} always
-{%     endif -%}
-{%   endfor -%}
-{% endfor -%}
-{% endif -%}
-{#- end isis default-information originate -#}
-{% if isis[dname]['redistribute'] is defined -%}
-{% for protocol in isis[dname]['redistribute']['ipv4']  -%}
-{%   for lvl in isis[dname]['redistribute']['ipv4'][protocol] -%}
-{%     if isis[dname]['redistribute']['ipv4'][protocol][lvl]['metric'] is defined -%}
- redistribute ipv4 {{ protocol }} {{ lvl | replace('_', '-') }} metric {{ isis[dname]['redistribute']['ipv4'][protocol][lvl]['metric'] }}
-{%     elif isis[dname]['redistribute']['ipv4'][protocol][lvl]['route_map'] is defined -%}
- redistribute ipv4 {{ protocol }} {{ lvl | replace('_', '-') }} route-map {{ isis[dname]['redistribute']['ipv4'][protocol][lvl]['route_map'] }}
-{%     else -%}
- redistribute ipv4 {{ protocol }} {{ lvl | replace('_', '-') }}
-{%     endif -%}
-{%   endfor -%}
-{% endfor -%}
-{% endif -%}
-{#- end isis redistribute -#}
-{% if isis[dname]['level'] -%}
-{%   if isis[dname]['level'] == 'level-1' -%}
+{% endif %}
+{% if domain_password is defined and domain_password.plaintext_password is defined and domain_password.plaintext_password is not none %}
+ domain-password clear {{ domain_password.plaintext_password }}
+{% endif %}
+{% if lsp_gen_interval is defined and lsp_gen_interval is not none %}
+ lsp-gen-interval {{ lsp_gen_interval }}
+{% endif %}
+{% if lsp_mtu is defined and lsp_mtu is not none %}
+ lsp-mtu {{ lsp_mtu }}
+{% endif %}
+{% if lsp_refresh_interval is defined and lsp_refresh_interval is not none %}
+ lsp-refresh-interval  {{ lsp_refresh_interval }}
+{% endif %}
+{% if max_lsp_lifetime is defined and max_lsp_lifetime is not none %}
+ max-lsp-lifetime {{ max_lsp_lifetime }}
+{% endif %}
+{% if spf_interval is defined and spf_interval is not none %}
+ spf-interval {{ spf_interval }}
+{% endif %}
+{% if spf_delay_ietf is defined and spf_delay_ietf.init_delay is defined and spf_delay_ietf.init_delay is not none %}
+ spf-delay-ietf init-delay {{ spf_delay_ietf.init_delay }}
+{% endif %}
+{% if area_password is defined and area_password.md5 is defined and area_password.md5 is not none %}
+ area-password md5 {{ area_password.md5 }}
+{% elif area_password is defined and area_password.plaintext_password is defined and area_password.plaintext_password is not none %}
+ area-password clear {{ area_password.plaintext_password }}
+{% endif %}
+{% if default_information is defined and default_information.originate is defined and default_information.originate is not none %}
+{%   for level in default_information.originate.ipv4 if default_information.originate.ipv4 is defined %}
+ default-information originate ipv4 {{ level | replace('_', '-') }}
+{%   endfor %}
+{%   for level in default_information.originate.ipv6 if default_information.originate.ipv6 is defined %}
+ default-information originate ipv6 {{ level | replace('_', '-') }} always
+{%   endfor %}
+{% endif %}
+{% if redistribute is defined and redistribute.ipv4 is defined and redistribute.ipv4 is not none %}
+{%   for protocol in redistribute.ipv4 %}
+{%     for level, level_config in redistribute.ipv4[protocol].items() %}
+{%       if level_config.metric is defined and level_config.metric is not none %}
+ redistribute ipv4 {{ protocol }} {{ level | replace('_', '-') }} metric {{ level_config.metric }}
+{%       elif level_config.route_map is defined and level_config.route_map is not none %}
+ redistribute ipv4 {{ protocol }} {{ level | replace('_', '-') }} route-map {{ level_config.route_map }}
+{%       else %}
+ redistribute ipv4 {{ protocol }} {{ level | replace('_', '-') }}
+{%       endif %}
+{%     endfor %}
+{%   endfor %}
+{% endif %}
+{% if level is defined and level is not none %}
+{%   if level == 'level-1' %}
  is-type level-1
-{%   elif isis[dname]['level'] == 'level-2'  -%}
+{%   elif level == 'level-2'  %}
  is-type level-2-only
-{%   elif isis[dname]['level'] == 'level-1-2'  -%}
+{%   elif level == 'level-1-2'  %}
  is-type level-1-2
-{%   endif -%}
-{% endif -%}
-{#- end isis level -#}
-{% if isis[dname]['spf_delay_ietf'] is defined -%}
-{%   if isis[dname]['spf_delay_ietf']['init_delay'] or isis[dname]['spf_delay_ietf']['short_delay'] or isis[dname]['spf_delay_ietf']['long_delay'] or isis[dname]['spf_delay_ietf']['holddown'] or isis[dname]['spf_delay_ietf']['time_to_learn'] -%}
- init-delay {{ isis[dname]['spf_delay_ietf']['init_delay'] }} short-delay {{ isis[dname]['spf_delay_ietf']['short_delay'] }} long-delay {{ isis[dname]['spf_delay_ietf']['long_delay'] }} holddown {{ isis[dname]['spf_delay_ietf']['holddown'] }} time-to-learn {{ isis[dname]['spf_delay_ietf']['time_to_learn'] }}
-{%   endif -%}
-{% endif -%}
+{%   endif %}
+{% endif %}
 !
-{% set isis_iface = isis[dname]['interface'] -%}
-{% if isis_iface is string -%}
-interface {{ isis_iface }} 
-ip router isis {{ dname }}
-{% else -%}
-{% for value in isis_iface -%}
-interface {{ value }} 
-ip router isis {{ dname }}
-{%   if 'bfd' in isis_iface[value] -%}
-  isis bfd
-{%   endif -%}
-{%   if 'point_to_point' in isis_iface[value]['network'] -%}
-  isis network point-to-point
-{%   endif -%}
-{%   if isis_iface[value]['circuit_type'] == 'level-1' -%}
- isis circuit-type level-1
-{%   elif isis_iface[value]['circuit_type'] == 'level-2-only' -%}
- isis circuit-type level-2-only
-{%   elif isis_iface[value]['circuit_type'] == 'level-1-2' -%}
- isis circuit-type level-1-2 
-{%   endif -%}
-{%   if isis_iface[value]['hello_interval'] -%}
- isis hello-interval {{ isis_iface[value]['hello_interval'] }}
-{%   endif -%}
-{%   if isis_iface[value]['hello_multiplier'] -%}
- isis hello-multiplier {{ isis_iface[value]['hello_multiplier'] }}
-{%   endif -%}
-{%   if 'hello_padding' in isis_iface[value] -%}
+{% if interface_remove is defined and interface_remove is not none %}
+{%   for iface in interface_remove %}
+interface {{ iface }}
+ no ip router isis
+{%   endfor %}
+{% endif %}
+!
+{% if interface is defined and interface is not none %}
+{%   for iface, iface_config in interface.items() %}
+interface {{ iface }}
+ ip router isis {{ process }}
+{%     if iface_config.bfd is defined %}
+ isis bfd
+{%     endif %}
+{%     if iface_config.network is defined and iface_config.network.point_to_point is defined %}
+ isis network point-to-point
+{%     endif %}
+{%     if iface_config.circuit_type is defined %}
+ isis circuit-type {{ iface_config.circuit_type }}
+{%     endif %}
+{%     if iface_config.hello_interval is defined and iface_config.hello_interval is not none %}
+ isis hello-interval {{ iface_config.hello_interval }}
+{%     endif %}
+{%     if iface_config.hello_multiplier is defined and iface_config.hello_multiplier is not none %}
+ isis hello-multiplier {{ iface_config.hello_multiplier }}
+{%     endif %}
+{%     if iface_config.hello_padding is defined %}
  isis hello padding
-{%   endif -%}
-{%   if isis_iface[value]['metric'] -%}
- isis metric {{ isis_iface[value]['metric'] }}
-{%   endif -%}
-{%   if 'passive' in isis_iface[value] -%}
+{%     endif %}
+{%     if iface_config.metric is defined and iface_config.metric is not none %}
+ isis metric {{ iface_config.metric }}
+{%     endif %}
+{%     if iface_config.passive is defined %}
  isis passive
-{%   endif -%}
-{%   if isis_iface[value]['password'] is defined -%}
-{%     if isis_iface[value]['password']['plaintext_password'] is defined -%}
- isis password clear {{ isis_iface[value]['password']['plaintext_password'] }}
-{%     endif -%}
-{%   endif -%}
-{%   if isis_iface[value]['priority'] -%}
- isis priority {{ isis_iface[value]['priority'] }}
-{%   endif -%}
-{%   if isis_iface[value]['psnp_interval'] -%}
- isis psnp-interval {{ isis_iface[value]['psnp_interval'] }}
-{%   endif -%}
-{%   if 'three_way_handshake' in isis_iface[value] -%}
+{%     endif %}
+{%     if iface_config.password is defined and iface_config.password.plaintext_password is defined and iface_config.password.plaintext_password is not none %}
+ isis password clear {{ iface_config.password.plaintext_password }}
+{%     endif %}
+{%     if iface_config.priority is defined and iface_config.priority is not none %}
+ isis priority {{ iface_config.priority }}
+{%     endif %}
+{%     if iface_config.psnp_interval is defined and iface_config.psnp_interval is not none %}
+ isis psnp-interval {{ iface_config.psnp_interval }}
+{%     endif %}
+{%     if iface_config.three_way_handshake is defined %}
  isis three-way-handshake
-{%   endif -%}
-{% endfor -%}
-{% endif -%}
-{#- end isis interface -#}
-!
-{%   endfor -%}
+{%     endif %}
+{%   endfor %}
+{% endif %}
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index 4ee6b31f6..d5e5b64fb 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -19,29 +19,33 @@ import os
 from sys import exit
 
 from vyos.config import Config
+from vyos.configdict import node_changed
 from vyos import ConfigError
 from vyos.util import call
 from vyos.template import render
 from vyos.template import render_to_string
 from vyos import frr
 from vyos import airbag
-from pprint import pprint
 airbag.enable()
 
 config_file = r'/tmp/isis.frr'
 
-default_config_data = {
-    'interface':  ''
-}
-
-def get_config():
-    conf = Config()
+def get_config(config=None):
+    if config:
+        conf = config
+    else:
+        conf = Config()
     base = ['protocols', 'isis']
-    isis = conf.get_config_dict(base, key_mangling=('-', '_'))
-    if not conf.exists(base):
-        isis = {}
 
-    pprint(isis)
+    isis = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+
+    # determine which members have been removed
+    for instance in isis:
+        conf.set_level(base + [instance])
+        tmp = node_changed(conf, ['interface'])
+        if tmp:
+            isis[instance].update({'interface_remove': tmp})
+
     return isis
 
 def verify(isis):
@@ -49,32 +53,31 @@ def verify(isis):
     if not isis:
         return None
 
-    for proc_id, foo_config in isis['isis'].items():
-
+    for process, isis_config in isis.items():
         # If more then one isis process is defined (Frr only supports one)
         # http://docs.frrouting.org/en/latest/isisd.html#isis-router
-        if len(isis['isis']) > 1:
+        if len(isis) > 1:
             raise ConfigError('Only one isis process can be definded')
 
         # If network entity title (net) not defined
-        if not "net" in  foo_config:
-          raise ConfigError('Define net format iso is mandatory in \"isis {} net"!'.format(proc_id))
+        if 'net' not in isis_config:
+            raise ConfigError('ISIS net format iso is mandatory!')
 
         # If interface not set
-        if not "interface" in  foo_config:
-          raise ConfigError('Define interface is mandatory in \"isis {} interface"!'.format(proc_id))
+        if 'interface' not in isis_config:
+            raise ConfigError('ISIS interface is mandatory!')
 
         # If md5 and plaintext-password set at the same time
-        if 'area_password' in foo_config:
-            if "md5" in foo_config['area_password'] and "plaintext_password" in foo_config['area_password']:
-                raise ConfigError('Only one password type should be used in \"isis {} area-password"!'.format(proc_id))
+        if 'area_password' in isis_config:
+            if {'md5', 'plaintext_password'} <= set(isis_config['encryption']):
+                raise ConfigError('Can not use both md5 and plaintext-password for ISIS area-password!')
 
         # If one param from deley set, but not set others
-        if 'spf_delay_ietf' in foo_config:
+        if 'spf_delay_ietf' in isis_config:
             required_timers = ['holddown', 'init_delay', 'long_delay', 'short_delay', 'time_to_learn']
             exist_timers = []
             for elm_timer in required_timers:
-                if elm_timer in foo_config['spf_delay_ietf']:
+                if elm_timer in isis_config['spf_delay_ietf']:
                     exist_timers.append(elm_timer)
 
             exist_timers = set(required_timers).difference(set(exist_timers))
@@ -82,14 +85,14 @@ def verify(isis):
                 raise ConfigError('All types of delay must be specified: ' + ', '.join(exist_timers).replace('_', '-'))
 
         # If Redistribute set, but level don't set
-        if 'redistribute' in foo_config:
-            proc_level = foo_config.get('level','').replace('-','_')
-            for proto, proto_config in foo_config.get('redistribute', {}).get('ipv4', {}).items():
+        if 'redistribute' in isis_config:
+            proc_level = isis_config.get('level','').replace('-','_')
+            for proto, proto_config in isis_config.get('redistribute', {}).get('ipv4', {}).items():
                 if 'level_1' not in proto_config and 'level_2' not in proto_config:
-                    raise ConfigError('Redistribute level-1 or level-2 should be specified in \"protocols isis {} redistribute ipv4 {}\"'.format(proc_id, proto))
+                    raise ConfigError('Redistribute level-1 or level-2 should be specified in \"protocols isis {} redistribute ipv4 {}\"'.format(process, proto))
             for redistribute_level in proto_config.keys():
                 if proc_level and proc_level != 'level_1_2' and proc_level != redistribute_level:
-                    raise ConfigError('\"protocols isis {0} redistribute ipv4 {2} {3}\" cannot be used with \"protocols isis {0} level {1}\"'.format(proc_id, proc_level, proto, redistribute_level))
+                    raise ConfigError('\"protocols isis {0} redistribute ipv4 {2} {3}\" cannot be used with \"protocols isis {0} level {1}\"'.format(process, proc_level, proto, redistribute_level))
 
     return None
 
@@ -98,31 +101,43 @@ def generate(isis):
         isis['new_frr_config'] = ''
         return None
 
+    # only one ISIS process is supported, so we can directly send the first key
+    # of the config dict
+    process = list(isis.keys())[0]
+    isis[process]['process'] = process
+
+    import pprint
+    pprint.pprint(isis[process])
+
     # render(config) not needed, its only for debug
-    render(config_file, 'frr/isis.frr.tmpl', isis)
+    render(config_file, 'frr/isis.frr.tmpl', isis[process], trim_blocks=True)
 
-    isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl', isis)
+    isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl',
+                                              isis[process], trim_blocks=True)
 
     return None
 
 def apply(isis):
 
     # Save original configration prior to starting any commit actions
-    isis['original_config'] = frr.get_configuration(daemon='isisd')
-    isis['modified_config'] = frr.replace_section(isis['original_config'], isis['new_frr_config'], from_re='router isis .*')
+    frr_cfg = {}
+    frr_cfg['original_config'] = frr.get_configuration(daemon='isisd')
+    frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], isis['new_frr_config'], from_re='router isis .*')
 
     # Debugging
     print('')
     print('--------- DEBUGGING ----------')
-    print(f'Existing config:\n{isis["original_config"]}\n\n')
+    print(f'Existing config:\n{frr_cfg["original_config"]}\n\n')
     print(f'Replacement config:\n{isis["new_frr_config"]}\n\n')
-    print(f'Modified config:\n{isis["modified_config"]}\n\n')
+    print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n')
 
-    # Frr Mark configuration will test for syntax errors and exception out if any syntax errors are detected
-    frr.mark_configuration(isis['modified_config'])
+    # FRR mark configuration will test for syntax errors and throws an
+    # exception if any syntax errors is detected
+    frr.mark_configuration(frr_cfg['modified_config'])
 
-    # Commit the resulting new configuration to frr, this will render an frr.CommitError() Exception on fail
-    frr.reload_configuration(isis['modified_config'], daemon='isisd')
+    # Commit resulting configuration to FRR, this will throw CommitError
+    # on failure
+    frr.reload_configuration(frr_cfg['modified_config'], daemon='isisd')
 
     return None
 
-- 
cgit v1.2.3