summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2021-01-24 13:14:48 +0100
committerChristian Poessinger <christian@poessinger.com>2021-01-24 14:43:53 +0100
commit42f91ee461589f1a407a2ceec8c63dd889f4a609 (patch)
tree00a70bacb97a86dd26b68a3296b5a4f503659caf
parent5834355b6342b7f9ab43da8e97b519f112693d09 (diff)
downloadvyos-1x-42f91ee461589f1a407a2ceec8c63dd889f4a609.tar.gz
vyos-1x-42f91ee461589f1a407a2ceec8c63dd889f4a609.zip
ospfv3: T3244: provide full protocol support in XML and Python
This commit provides the implementation of the OSPFv3 (IPv6) CLI with a Jinja2 template that is loaded by FRR reload. It also contains some initial smoketests. There is yet no verify() implementation!
-rw-r--r--Makefile1
-rw-r--r--data/configd-include.json1
-rw-r--r--data/templates/frr/ospfv3.frr.tmpl41
-rw-r--r--interface-definitions/protocols-ospfv3.xml.in19
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py109
5 files changed, 164 insertions, 7 deletions
diff --git a/Makefile b/Makefile
index f84e8c8ca..66b1e8bb7 100644
--- a/Makefile
+++ b/Makefile
@@ -47,7 +47,6 @@ interface_definitions: $(config_xml_obj)
rm -f $(TMPL_DIR)/vpn/node.def
rm -f $(TMPL_DIR)/vpn/ipsec/node.def
rm -rf $(TMPL_DIR)/vpn/nipsec
- rm -rf $(TMPL_DIR)/protocols/nospfv3
# XXX: required until OSPF and RIP is migrated from vyatta-cfg-quagga to vyos-1x
mkdir $(TMPL_DIR)/interfaces/loopback/node.tag/ipv6
diff --git a/data/configd-include.json b/data/configd-include.json
index 345460700..751d8e012 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -36,6 +36,7 @@
"protocols_isis.py",
"protocols_mpls.py",
"protocols_ospf.py",
+"protocols_ospfv3.py",
"protocols_pim.py",
"protocols_rip.py",
"protocols_static_multicast.py",
diff --git a/data/templates/frr/ospfv3.frr.tmpl b/data/templates/frr/ospfv3.frr.tmpl
new file mode 100644
index 000000000..c63ef80dc
--- /dev/null
+++ b/data/templates/frr/ospfv3.frr.tmpl
@@ -0,0 +1,41 @@
+!
+router ospf6
+{% if area is defined and area is not none %}
+{% for area_id, area_config in area.items() %}
+{% if area_config.interface is defined and area_config.interface is not none %}
+{% for interface in area_config.interface %}
+ interface {{ interface }} area {{ area_id }}
+{% endfor %}
+{% endif %}
+{% if area_config.range is defined and area_config.range is not none %}
+{% for prefix, prefix_config in area_config.range.items() %}
+ area {{ area_id }} range {{ prefix }} {{ 'advertise' if prefix_config.advertise is defined }} {{ 'not-advertise' if prefix_config.not_advertise is defined }}
+{% endfor %}
+{% endif %}
+{% if area_config.export_list is defined and area_config.export_list is not none %}
+ area {{ area_id }} export-list {{ area_config.export_list }}
+{% endif %}
+{% if area_config.import_list is defined and area_config.import_list is not none %}
+ area {{ area_id }} import-list {{ area_config.import_list }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if distance is defined and distance is not none %}
+{% if distance.global is defined and distance.global is not none %}
+ distance {{ distance.global }}
+{% endif %}
+{% if distance.ospfv3 is defined and distance.ospfv3 is not none %}
+ distance ospf6 {{ 'intra-area ' + distance.ospfv3.intra_area if distance.ospfv3.intra_area is defined }} {{ 'inter-area ' + distance.ospfv3.inter_area if distance.ospfv3.inter_area is defined }} {{ 'external ' + distance.ospfv3.external if distance.ospfv3.external is defined }}
+{% endif %}
+{% endif %}
+{% if parameters is defined and parameters is not none %}
+{% if parameters.router_id is defined and parameters.router_id is not none %}
+ ospf6 router-id {{ parameters.router_id }}
+{% endif %}
+{% endif %}
+{% if redistribute is defined and redistribute is not none %}
+{% for protocol, options in redistribute.items() %}
+ redistribute {{ protocol }} {{ 'route-map ' + options.route_map if options.route_map is defined }}
+{% endfor %}
+{% endif %}
+!
diff --git a/interface-definitions/protocols-ospfv3.xml.in b/interface-definitions/protocols-ospfv3.xml.in
index 8ade1433d..7f80f9f9d 100644
--- a/interface-definitions/protocols-ospfv3.xml.in
+++ b/interface-definitions/protocols-ospfv3.xml.in
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Protocol OSPFv3 configuration -->
<interfaceDefinition>
<node name="protocols">
<children>
- <node name="nospfv3" owner="${vyos_conf_scripts_dir}/protocols_ospfv3.py">
+ <node name="ospfv3" owner="${vyos_conf_scripts_dir}/protocols_ospfv3.py">
<properties>
- <help>IPv6 Open Shortest Path First protocol (OSPFv3) parameters</help>
+ <help>Open Shortest Path First (OSPF) for IPv6</help>
<priority>620</priority>
</properties>
<children>
@@ -14,11 +13,11 @@
<help>OSPFv3 Area</help>
<valueHelp>
<format>u32</format>
- <description>OSPFv3 area in decimal notation</description>
+ <description>Area ID as a decimal value</description>
</valueHelp>
<valueHelp>
<format>ipv4</format>
- <description>OSPFv3 area in dotted decimal notation</description>
+ <description>Area ID in IP address forma</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-4294967295"/>
@@ -44,10 +43,18 @@
</leafNode>
<leafNode name="interface">
<properties>
- <help>OSPFv3 area interface</help>
+ <help>Enable routing on an IPv6 interface</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces.py</script>
</completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface used for routing information exchange</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(br|bond|dum|en|eth|gnv|peth|tun|vti|vxlan|wg|wlan)[0-9]+|lo$</regex>
+ </constraint>
+ <multi/>
</properties>
</leafNode>
<tagNode name="range">
diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
new file mode 100755
index 000000000..f514641ef
--- /dev/null
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+from sys import exit
+
+from vyos.config import Config
+from vyos.configdict import dict_merge
+from vyos.template import render
+from vyos.template import render_to_string
+from vyos.util import call
+from vyos.util import dict_search
+from vyos.xml import defaults
+from vyos import ConfigError
+from vyos import frr
+from vyos import airbag
+airbag.enable()
+
+config_file = r'/tmp/ospfv3.frr'
+frr_daemon = 'ospf6d'
+
+DEBUG = os.path.exists('/tmp/ospfv3.debug')
+if DEBUG:
+ import logging
+ lg = logging.getLogger("vyos.frr")
+ lg.setLevel(logging.DEBUG)
+ ch = logging.StreamHandler()
+ lg.addHandler(ch)
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['protocols', 'ospfv3']
+ ospfv3 = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ return ospfv3
+
+def verify(ospfv3):
+ if not ospfv3:
+ return None
+
+ return None
+
+def generate(ospfv3):
+ if not ospfv3:
+ ospfv3['new_frr_config'] = ''
+ return None
+
+ # render(config) not needed, its only for debug
+ render(config_file, 'frr/ospfv3.frr.tmpl', ospfv3)
+ ospfv3['new_frr_config'] = render_to_string('frr/ospfv3.frr.tmpl', ospfv3)
+
+ return None
+
+def apply(ospfv3):
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+ frr_cfg.load_configuration(frr_daemon)
+ frr_cfg.modify_section('router ospf6', '')
+ frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospfv3['new_frr_config'])
+
+ # Debugging
+ if DEBUG:
+ from pprint import pprint
+ print('')
+ print('--------- DEBUGGING ----------')
+ pprint(dir(frr_cfg))
+ print('Existing config:\n')
+ for line in frr_cfg.original_config:
+ print(line)
+ print(f'Replacement config:\n')
+ print(f'{ospfv3["new_frr_config"]}')
+ print(f'Modified config:\n')
+ print(f'{frr_cfg}')
+
+ frr_cfg.commit_configuration(frr_daemon)
+
+ # If FRR config is blank, re-run the blank commit x times due to frr-reload
+ # behavior/bug not properly clearing out on one commit.
+ if ospfv3['new_frr_config'] == '':
+ for a in range(5):
+ frr_cfg.commit_configuration(frr_daemon)
+
+ return None
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)