summaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/helper.py24
-rw-r--r--src/tests/test_config_diff.py69
-rw-r--r--src/tests/test_config_parser.py55
-rw-r--r--src/tests/test_configverify.py33
-rw-r--r--src/tests/test_dependency_graph.py31
-rw-r--r--src/tests/test_dict_search.py84
-rw-r--r--src/tests/test_find_device_file.py35
-rw-r--r--src/tests/test_initial_setup.py101
-rw-r--r--src/tests/test_op_mode.py65
-rw-r--r--src/tests/test_task_scheduler.py129
-rw-r--r--src/tests/test_template.py194
-rw-r--r--src/tests/test_utils.py28
-rw-r--r--src/tests/test_utils_network.py50
13 files changed, 898 insertions, 0 deletions
diff --git a/src/tests/helper.py b/src/tests/helper.py
new file mode 100644
index 0000000..f703314
--- /dev/null
+++ b/src/tests/helper.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import importlib.util
+
+def prepare_module(file_path='', module_name=''):
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[module_name] = module
diff --git a/src/tests/test_config_diff.py b/src/tests/test_config_diff.py
new file mode 100644
index 0000000..61a2f34
--- /dev/null
+++ b/src/tests/test_config_diff.py
@@ -0,0 +1,69 @@
+#!/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/>.
+
+import vyos.configtree
+
+from unittest import TestCase
+
+class TestConfigDiff(TestCase):
+ def setUp(self):
+ with open('tests/data/config.left', 'r') as f:
+ config_string = f.read()
+ self.config_left = vyos.configtree.ConfigTree(config_string)
+
+ with open('tests/data/config.right', 'r') as f:
+ config_string = f.read()
+ self.config_right = vyos.configtree.ConfigTree(config_string)
+
+ self.config_null = vyos.configtree.ConfigTree('')
+
+ def test_unit(self):
+ diff = vyos.configtree.DiffTree(self.config_left, self.config_null)
+ sub = diff.sub
+ self.assertEqual(sub.to_string(), self.config_left.to_string())
+
+ diff = vyos.configtree.DiffTree(self.config_null, self.config_left)
+ add = diff.add
+ self.assertEqual(add.to_string(), self.config_left.to_string())
+
+ def test_symmetry(self):
+ lr_diff = vyos.configtree.DiffTree(self.config_left,
+ self.config_right)
+ rl_diff = vyos.configtree.DiffTree(self.config_right,
+ self.config_left)
+
+ sub = lr_diff.sub
+ add = rl_diff.add
+ self.assertEqual(sub.to_string(), add.to_string())
+ add = lr_diff.add
+ sub = rl_diff.sub
+ self.assertEqual(add.to_string(), sub.to_string())
+
+ def test_identity(self):
+ lr_diff = vyos.configtree.DiffTree(self.config_left,
+ self.config_right)
+
+ sub = lr_diff.sub
+ inter = lr_diff.inter
+ add = lr_diff.add
+
+ r_union = vyos.configtree.union(add, inter)
+ l_union = vyos.configtree.union(sub, inter)
+
+ self.assertEqual(r_union.to_string(),
+ self.config_right.to_string(ordered_values=True))
+ self.assertEqual(l_union.to_string(),
+ self.config_left.to_string(ordered_values=True))
diff --git a/src/tests/test_config_parser.py b/src/tests/test_config_parser.py
new file mode 100644
index 0000000..c69732d
--- /dev/null
+++ b/src/tests/test_config_parser.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018-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 vyos.configtree
+
+from unittest import TestCase
+
+class TestConfigParser(TestCase):
+ def setUp(self):
+ with open('tests/data/config.valid', 'r') as f:
+ config_string = f.read()
+ self.config = vyos.configtree.ConfigTree(config_string)
+
+ def test_top_level_valueless(self):
+ self.assertTrue(self.config.exists(["top-level-valueless-node"]))
+
+ def test_top_level_leaf(self):
+ self.assertTrue(self.config.exists(["top-level-leaf-node"]))
+ self.assertEqual(self.config.return_value(["top-level-leaf-node"]), "foo")
+
+ def test_top_level_tag(self):
+ self.assertTrue(self.config.exists(["top-level-tag-node"]))
+ # Sorting is now intentional, during parsing of config
+ self.assertEqual(self.config.list_nodes(["top-level-tag-node"]), ["bar", "foo"])
+
+ def test_copy(self):
+ self.config.copy(["top-level-tag-node", "bar"], ["top-level-tag-node", "baz"])
+ print(self.config.to_string())
+ self.assertTrue(self.config.exists(["top-level-tag-node", "baz"]))
+
+ def test_copy_duplicate(self):
+ with self.assertRaises(vyos.configtree.ConfigTreeError):
+ self.config.copy(["top-level-tag-node", "foo"], ["top-level-tag-node", "bar"])
+
+ def test_rename(self):
+ self.config.rename(["top-level-tag-node", "bar"], "quux")
+ print(self.config.to_string())
+ self.assertTrue(self.config.exists(["top-level-tag-node", "quux"]))
+
+ def test_rename_duplicate(self):
+ with self.assertRaises(vyos.configtree.ConfigTreeError):
+ self.config.rename(["top-level-tag-node", "foo"], "bar")
diff --git a/src/tests/test_configverify.py b/src/tests/test_configverify.py
new file mode 100644
index 0000000..15ccdf1
--- /dev/null
+++ b/src/tests/test_configverify.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from unittest import TestCase
+from vyos.configverify import verify_diffie_hellman_length
+from vyos.utils.process import cmd
+
+dh_file = '/tmp/dh.pem'
+
+class TestDictSearch(TestCase):
+ def setUp(self):
+ pass
+
+ def test_dh_key_none(self):
+ self.assertFalse(verify_diffie_hellman_length('/tmp/non_existing_file', '1024'))
+
+ def test_dh_key_512(self):
+ key_len = '512'
+ cmd(f'openssl dhparam -out {dh_file} {key_len}')
+ self.assertTrue(verify_diffie_hellman_length(dh_file, key_len))
diff --git a/src/tests/test_dependency_graph.py b/src/tests/test_dependency_graph.py
new file mode 100644
index 0000000..f682e87
--- /dev/null
+++ b/src/tests/test_dependency_graph.py
@@ -0,0 +1,31 @@
+#!/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/>.
+
+import os
+from vyos.configdep import check_dependency_graph
+
+_here = os.path.dirname(__file__)
+ddir = os.path.join(_here, '../../data/config-mode-dependencies')
+
+from unittest import TestCase
+
+class TestDependencyGraph(TestCase):
+ def setUp(self):
+ pass
+
+ def test_acyclic(self):
+ res = check_dependency_graph(dependency_dir=ddir)
+ self.assertTrue(res)
diff --git a/src/tests/test_dict_search.py b/src/tests/test_dict_search.py
new file mode 100644
index 0000000..2435d89
--- /dev/null
+++ b/src/tests/test_dict_search.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from unittest import TestCase
+from vyos.utils.dict import dict_search
+from vyos.utils.dict import dict_search_recursive
+
+data = {
+ 'string': 'fooo',
+ 'nested': {'string': 'bar', 'empty': '', 'list': ['foo', 'bar']},
+ 'non': {},
+ 'list': ['bar', 'baz'],
+ 'dict': {'key_1': {}, 'key_2': 'vyos'},
+ 'interfaces': {'dummy': {'dum0': {'address': ['192.0.2.17/29']}},
+ 'ethernet': {'eth0': {'address': ['2001:db8::1/64', '192.0.2.1/29'],
+ 'description': 'Test123',
+ 'duplex': 'auto',
+ 'hw_id': '00:00:00:00:00:01',
+ 'speed': 'auto'},
+ 'eth1': {'address': ['192.0.2.9/29'],
+ 'description': 'Test456',
+ 'duplex': 'auto',
+ 'hw_id': '00:00:00:00:00:02',
+ 'speed': 'auto'}}}
+}
+
+class TestDictSearch(TestCase):
+ def setUp(self):
+ pass
+
+ def test_non_existing_keys(self):
+ # TestDictSearch: Return False when querying for non-existent key
+ self.assertEqual(dict_search('non_existing', data), None)
+ self.assertEqual(dict_search('non.existing.fancy.key', data), None)
+
+ def test_string(self):
+ # TestDictSearch: Return value when querying string
+ self.assertEqual(dict_search('string', data), data['string'])
+
+ def test_list(self):
+ # TestDictSearch: Return list items when querying list
+ self.assertEqual(dict_search('list', data), data['list'])
+
+ def test_dict_key_value(self):
+ # TestDictSearch: Return dictionary keys value when value is present
+ self.assertEqual(dict_search('dict.key_2', data), data['dict']['key_2'])
+
+ def test_nested_dict_key_value(self):
+ # TestDictSearch: Return string value of last key when querying for a nested string
+ self.assertEqual(dict_search('nested.string', data), data['nested']['string'])
+
+ def test_nested_dict_key_empty(self):
+ # TestDictSearch: Return False when querying for a nested string whose last key is empty
+ self.assertEqual(dict_search('nested.empty', data), '')
+ self.assertFalse(dict_search('nested.empty', data))
+
+ def test_nested_list(self):
+ # TestDictSearch: Return list items when querying nested list
+ self.assertEqual(dict_search('nested.list', data), data['nested']['list'])
+
+ def test_invalid_input(self):
+ # TestDictSearch: Return list items when querying nested list
+ self.assertEqual(dict_search('nested.list', None), None)
+ self.assertEqual(dict_search(None, data), None)
+
+ def test_dict_search_recursive(self):
+ # Test nested search in dictionary
+ tmp = list(dict_search_recursive(data, 'hw_id'))
+ self.assertEqual(len(tmp), 2)
+ tmp = list(dict_search_recursive(data, 'address'))
+ self.assertEqual(len(tmp), 3)
diff --git a/src/tests/test_find_device_file.py b/src/tests/test_find_device_file.py
new file mode 100644
index 0000000..f18043d
--- /dev/null
+++ b/src/tests/test_find_device_file.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from unittest import TestCase
+from vyos.utils.system import find_device_file
+
+class TestDeviceFile(TestCase):
+ """ used to find USB devices on target """
+ def setUp(self):
+ pass
+
+ def test_null(self):
+ self.assertEqual(find_device_file('null'), '/dev/null')
+
+ def test_zero(self):
+ self.assertEqual(find_device_file('zero'), '/dev/zero')
+
+ def test_input_event(self):
+ self.assertEqual(find_device_file('event0'), '/dev/input/event0')
+
+ def test_non_existing(self):
+ self.assertFalse(find_device_file('vyos'))
diff --git a/src/tests/test_initial_setup.py b/src/tests/test_initial_setup.py
new file mode 100644
index 0000000..f85bf12
--- /dev/null
+++ b/src/tests/test_initial_setup.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018-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 unittest
+import vyos.configtree
+import vyos.initialsetup as vis
+
+from unittest import TestCase
+from vyos.xml_ref import definition
+from vyos.xml_ref.pkg_cache.vyos_1x_cache import reference
+
+class TestInitialSetup(TestCase):
+ def setUp(self):
+ with open('tests/data/config.boot.default', 'r') as f:
+ config_string = f.read()
+ self.config = vyos.configtree.ConfigTree(config_string)
+ self.xml = definition.Xml()
+ self.xml.define(reference)
+
+ def test_set_user_password(self):
+ vis.set_user_password(self.config, 'vyos', 'vyosvyos')
+
+ # Old password hash from the default config
+ old_pw = '$6$QxPS.uk6mfo$9QBSo8u1FkH16gMyAVhus6fU3LOzvLR9Z9.82m3tiHFAxTtIkhaZSWssSgzt4v4dGAL8rhVQxTg0oAG9/q11h/'
+ new_pw = self.config.return_value(["system", "login", "user", "vyos", "authentication", "encrypted-password"])
+
+ # Just check it changed the hash, don't try to check if hash is good
+ self.assertNotEqual(old_pw, new_pw)
+
+ def test_disable_user_password(self):
+ vis.disable_user_password(self.config, 'vyos')
+ new_pw = self.config.return_value(["system", "login", "user", "vyos", "authentication", "encrypted-password"])
+
+ self.assertEqual(new_pw, '!')
+
+ def test_set_ssh_key_with_name(self):
+ test_ssh_key = " ssh-rsa fakedata vyos@vyos "
+ vis.set_user_ssh_key(self.config, 'vyos', test_ssh_key)
+
+ key_type = self.config.return_value(["system", "login", "user", "vyos", "authentication", "public-keys", "vyos@vyos", "type"])
+ key_data = self.config.return_value(["system", "login", "user", "vyos", "authentication", "public-keys", "vyos@vyos", "key"])
+
+ self.assertEqual(key_type, 'ssh-rsa')
+ self.assertEqual(key_data, 'fakedata')
+ self.assertTrue(self.xml.is_tag(["system", "login", "user", "vyos", "authentication", "public-keys"]))
+
+ def test_set_ssh_key_without_name(self):
+ # If key file doesn't include a name, the function will use user name for the key name
+
+ test_ssh_key = " ssh-rsa fakedata "
+ vis.set_user_ssh_key(self.config, 'vyos', test_ssh_key)
+
+ key_type = self.config.return_value(["system", "login", "user", "vyos", "authentication", "public-keys", "vyos", "type"])
+ key_data = self.config.return_value(["system", "login", "user", "vyos", "authentication", "public-keys", "vyos", "key"])
+
+ self.assertEqual(key_type, 'ssh-rsa')
+ self.assertEqual(key_data, 'fakedata')
+ self.assertTrue(self.xml.is_tag(["system", "login", "user", "vyos", "authentication", "public-keys"]))
+
+ def test_create_user(self):
+ vis.create_user(self.config, 'jrandomhacker', password='qwerty', key=" ssh-rsa fakedata jrandomhacker@foovax ")
+
+ self.assertTrue(self.config.exists(["system", "login", "user", "jrandomhacker"]))
+ self.assertTrue(self.config.exists(["system", "login", "user", "jrandomhacker", "authentication", "public-keys", "jrandomhacker@foovax"]))
+ self.assertTrue(self.config.exists(["system", "login", "user", "jrandomhacker", "authentication", "encrypted-password"]))
+ self.assertEqual(self.config.return_value(["system", "login", "user", "jrandomhacker", "level"]), "admin")
+
+ def test_set_hostname(self):
+ vis.set_host_name(self.config, "vyos-test")
+
+ self.assertEqual(self.config.return_value(["system", "host-name"]), "vyos-test")
+
+ def test_set_name_servers(self):
+ vis.set_name_servers(self.config, ["192.0.2.10", "203.0.113.20"])
+ servers = self.config.return_values(["system", "name-server"])
+
+ self.assertIn("192.0.2.10", servers)
+ self.assertIn("203.0.113.20", servers)
+
+ def test_set_gateway(self):
+ vis.set_default_gateway(self.config, '192.0.2.1')
+
+ self.assertTrue(self.config.exists(['protocols', 'static', 'route', '0.0.0.0/0', 'next-hop', '192.0.2.1']))
+ self.assertTrue(self.xml.is_tag(['protocols', 'static', 'multicast', 'route', '0.0.0.0/0', 'next-hop']))
+ self.assertTrue(self.xml.is_tag(['protocols', 'static', 'multicast', 'route']))
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/tests/test_op_mode.py b/src/tests/test_op_mode.py
new file mode 100644
index 0000000..90963b3
--- /dev/null
+++ b/src/tests/test_op_mode.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 unittest import TestCase
+
+import vyos.opmode
+
+class TestVyOSOpMode(TestCase):
+ def test_field_name_normalization(self):
+ from vyos.opmode import _normalize_field_name
+
+ self.assertEqual(_normalize_field_name(" foo bar "), "foo_bar")
+ self.assertEqual(_normalize_field_name("foo-bar"), "foo_bar")
+ self.assertEqual(_normalize_field_name("foo (bar) baz"), "foo_bar_baz")
+ self.assertEqual(_normalize_field_name("load%"), "load_percentage")
+
+ def test_dict_fields_normalization_non_unique(self):
+ from vyos.opmode import _normalize_field_names
+
+ # Space and dot are both replaced by an underscore,
+ # so dicts like this cannor be normalized uniquely
+ data = {"foo bar": True, "foo.bar": False}
+
+ with self.assertRaises(vyos.opmode.InternalError):
+ _normalize_field_names(data)
+
+ def test_dict_fields_normalization_simple_dict(self):
+ from vyos.opmode import _normalize_field_names
+
+ data = {"foo bar": True, "Bar-Baz": False}
+ self.assertEqual(_normalize_field_names(data), {"foo_bar": True, "bar_baz": False})
+
+ def test_dict_fields_normalization_nested_dict(self):
+ from vyos.opmode import _normalize_field_names
+
+ data = {"foo bar": True, "bar-baz": {"baz-quux": {"quux-xyzzy": False}}}
+ self.assertEqual(_normalize_field_names(data),
+ {"foo_bar": True, "bar_baz": {"baz_quux": {"quux_xyzzy": False}}})
+
+ def test_dict_fields_normalization_mixed(self):
+ from vyos.opmode import _normalize_field_names
+
+ data = [{"foo bar": True, "bar-baz": [{"baz-quux": {"quux-xyzzy": [False]}}]}]
+ self.assertEqual(_normalize_field_names(data),
+ [{"foo_bar": True, "bar_baz": [{"baz_quux": {"quux_xyzzy": [False]}}]}])
+
+ def test_dict_fields_normalization_primitive(self):
+ from vyos.opmode import _normalize_field_names
+
+ data = [1, False, "foo"]
+ self.assertEqual(_normalize_field_names(data), [1, False, "foo"])
+
diff --git a/src/tests/test_task_scheduler.py b/src/tests/test_task_scheduler.py
new file mode 100644
index 0000000..130f825
--- /dev/null
+++ b/src/tests/test_task_scheduler.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018-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/>.
+
+import os
+import tempfile
+import unittest
+import importlib
+
+from vyos import ConfigError
+
+try:
+ task_scheduler = importlib.import_module("src.conf_mode.system_task-scheduler")
+except ModuleNotFoundError: # for unittest.main()
+ import sys
+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
+ task_scheduler = importlib.import_module("src.conf_mode.system_task-scheduler")
+
+class TestUpdateCrontab(unittest.TestCase):
+
+ def test_verify(self):
+ tests = [
+ {'name': 'one_task',
+ 'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
+ 'expected': None
+ },
+ {'name': 'has_interval_and_spec',
+ 'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '0 * * * *', 'executable': '/bin/ls', 'args': '-l'}],
+ 'expected': ConfigError
+ },
+ {'name': 'has_no_interval_and_spec',
+ 'tasks': [{'name': 'aaa', 'interval': '', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
+ 'expected': ConfigError
+ },
+ {'name': 'invalid_interval',
+ 'tasks': [{'name': 'aaa', 'interval': '1y', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
+ 'expected': ConfigError
+ },
+ {'name': 'invalid_interval_min',
+ 'tasks': [{'name': 'aaa', 'interval': '61m', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
+ 'expected': ConfigError
+ },
+ {'name': 'invalid_interval_hour',
+ 'tasks': [{'name': 'aaa', 'interval': '25h', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
+ 'expected': ConfigError
+ },
+ {'name': 'invalid_interval_day',
+ 'tasks': [{'name': 'aaa', 'interval': '32d', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
+ 'expected': ConfigError
+ },
+ {'name': 'no_executable',
+ 'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '', 'executable': '', 'args': ''}],
+ 'expected': ConfigError
+ },
+ {'name': 'invalid_executable',
+ 'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '', 'executable': '/bin/aaa', 'args': ''}],
+ 'expected': ConfigError
+ }
+ ]
+ for t in tests:
+ with self.subTest(msg=t['name'], tasks=t['tasks'], expected=t['expected']):
+ if t['expected'] is not None:
+ with self.assertRaises(t['expected']):
+ task_scheduler.verify(t['tasks'])
+ else:
+ task_scheduler.verify(t['tasks'])
+
+ def test_generate(self):
+ tests = [
+ {'name': 'zero_task',
+ 'tasks': [],
+ 'expected': []
+ },
+ {'name': 'one_task',
+ 'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
+ 'expected': [
+ '### Generated by vyos-update-crontab.py ###',
+ '*/60 * * * * root sg vyattacfg \"/bin/ls -l\"']
+ },
+ {'name': 'one_task_with_hour',
+ 'tasks': [{'name': 'aaa', 'interval': '10h', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
+ 'expected': [
+ '### Generated by vyos-update-crontab.py ###',
+ '0 */10 * * * root sg vyattacfg \"/bin/ls -l\"']
+ },
+ {'name': 'one_task_with_day',
+ 'tasks': [{'name': 'aaa', 'interval': '10d', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
+ 'expected': [
+ '### Generated by vyos-update-crontab.py ###',
+ '0 0 */10 * * root sg vyattacfg \"/bin/ls -l\"']
+ },
+ {'name': 'multiple_tasks',
+ 'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '', 'executable': '/bin/ls', 'args': '-l'},
+ {'name': 'bbb', 'interval': '', 'spec': '0 0 * * *', 'executable': '/bin/ls', 'args': '-ltr'}
+ ],
+ 'expected': [
+ '### Generated by vyos-update-crontab.py ###',
+ '*/60 * * * * root sg vyattacfg \"/bin/ls -l\"',
+ '0 0 * * * root sg vyattacfg \"/bin/ls -ltr\"']
+ }
+ ]
+ for t in tests:
+ with self.subTest(msg=t['name'], tasks=t['tasks'], expected=t['expected']):
+ task_scheduler.crontab_file = tempfile.mkstemp()[1]
+ task_scheduler.generate(t['tasks'])
+ if len(t['expected']) > 0:
+ self.assertTrue(os.path.isfile(task_scheduler.crontab_file))
+ with open(task_scheduler.crontab_file) as f:
+ actual = f.read()
+ self.assertEqual(t['expected'], actual.splitlines())
+ os.remove(task_scheduler.crontab_file)
+ else:
+ self.assertFalse(os.path.isfile(task_scheduler.crontab_file))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/tests/test_template.py b/src/tests/test_template.py
new file mode 100644
index 0000000..dbb86b4
--- /dev/null
+++ b/src/tests/test_template.py
@@ -0,0 +1,194 @@
+#!/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 vyos.template
+
+from vyos.utils.network import interface_exists
+from ipaddress import ip_network
+from unittest import TestCase
+
+class TestVyOSTemplate(TestCase):
+ def setUp(self):
+ pass
+
+ def test_is_interface(self):
+ for interface in ['lo', 'eth0']:
+ if interface_exists(interface):
+ self.assertTrue(vyos.template.is_interface(interface))
+ else:
+ self.assertFalse(vyos.template.is_interface(interface))
+ self.assertFalse(vyos.template.is_interface('non-existent'))
+
+ def test_is_ip(self):
+ self.assertTrue(vyos.template.is_ip('192.0.2.1'))
+ self.assertTrue(vyos.template.is_ip('2001:db8::1'))
+ self.assertFalse(vyos.template.is_ip('VyOS'))
+
+ def test_is_ipv4(self):
+ self.assertTrue(vyos.template.is_ipv4('192.0.2.1'))
+ self.assertTrue(vyos.template.is_ipv4('192.0.2.0/24'))
+ self.assertTrue(vyos.template.is_ipv4('192.0.2.1/32'))
+
+ self.assertFalse(vyos.template.is_ipv4('2001:db8::1'))
+ self.assertFalse(vyos.template.is_ipv4('2001:db8::/64'))
+ self.assertFalse(vyos.template.is_ipv4('VyOS'))
+
+ def test_is_ipv6(self):
+ self.assertTrue(vyos.template.is_ipv6('2001:db8::1'))
+ self.assertTrue(vyos.template.is_ipv6('2001:db8::/64'))
+ self.assertTrue(vyos.template.is_ipv6('2001:db8::1/64'))
+
+ self.assertFalse(vyos.template.is_ipv6('192.0.2.1'))
+ self.assertFalse(vyos.template.is_ipv6('192.0.2.0/24'))
+ self.assertFalse(vyos.template.is_ipv6('192.0.2.1/32'))
+ self.assertFalse(vyos.template.is_ipv6('VyOS'))
+
+ def test_address_from_cidr(self):
+ self.assertEqual(vyos.template.address_from_cidr('192.0.2.0/24'), '192.0.2.0')
+ self.assertEqual(vyos.template.address_from_cidr('2001:db8::/48'), '2001:db8::')
+
+ with self.assertRaises(ValueError):
+ # ValueError: 192.0.2.1/24 has host bits set
+ self.assertEqual(vyos.template.address_from_cidr('192.0.2.1/24'), '192.0.2.1')
+
+ with self.assertRaises(ValueError):
+ # ValueError: 2001:db8::1/48 has host bits set
+ self.assertEqual(vyos.template.address_from_cidr('2001:db8::1/48'), '2001:db8::1')
+
+ network_v4 = '192.0.2.0/26'
+ self.assertEqual(vyos.template.address_from_cidr(network_v4), str(ip_network(network_v4).network_address))
+
+ def test_netmask_from_cidr(self):
+ self.assertEqual(vyos.template.netmask_from_cidr('192.0.2.0/24'), '255.255.255.0')
+ self.assertEqual(vyos.template.netmask_from_cidr('192.0.2.128/25'), '255.255.255.128')
+ self.assertEqual(vyos.template.netmask_from_cidr('2001:db8::/48'), 'ffff:ffff:ffff::')
+
+ with self.assertRaises(ValueError):
+ # ValueError: 192.0.2.1/24 has host bits set
+ self.assertEqual(vyos.template.netmask_from_cidr('192.0.2.1/24'), '255.255.255.0')
+
+ with self.assertRaises(ValueError):
+ # ValueError: 2001:db8:1:/64 has host bits set
+ self.assertEqual(vyos.template.netmask_from_cidr('2001:db8:1:/64'), 'ffff:ffff:ffff:ffff::')
+
+ network_v4 = '192.0.2.0/26'
+ self.assertEqual(vyos.template.netmask_from_cidr(network_v4), str(ip_network(network_v4).netmask))
+
+ def test_first_host_address(self):
+ self.assertEqual(vyos.template.first_host_address('10.0.0.0/24'), '10.0.0.1')
+ self.assertEqual(vyos.template.first_host_address('10.0.0.10/24'), '10.0.0.1')
+ self.assertEqual(vyos.template.first_host_address('10.0.0.255/24'), '10.0.0.1')
+ self.assertEqual(vyos.template.first_host_address('10.0.0.128/25'), '10.0.0.129')
+ self.assertEqual(vyos.template.first_host_address('2001:db8::/64'), '2001:db8::1')
+ self.assertEqual(vyos.template.first_host_address('2001:db8::1000/64'), '2001:db8::1')
+ self.assertEqual(vyos.template.first_host_address('2001:db8::ffff:ffff:ffff:ffff/64'), '2001:db8::1')
+
+ def test_last_host_address(self):
+ self.assertEqual(vyos.template.last_host_address('10.0.0.0/24'), '10.0.0.254')
+ self.assertEqual(vyos.template.last_host_address('10.0.0.128/25'), '10.0.0.254')
+ self.assertEqual(vyos.template.last_host_address('2001:db8::/64'), '2001:db8::ffff:ffff:ffff:ffff')
+
+ def test_increment_ip(self):
+ self.assertEqual(vyos.template.inc_ip('10.0.0.0/24', '2'), '10.0.0.2')
+ self.assertEqual(vyos.template.inc_ip('10.0.0.0', '2'), '10.0.0.2')
+ self.assertEqual(vyos.template.inc_ip('10.0.0.0', '10'), '10.0.0.10')
+ self.assertEqual(vyos.template.inc_ip('2001:db8::/64', '2'), '2001:db8::2')
+ self.assertEqual(vyos.template.inc_ip('2001:db8::', '10'), '2001:db8::a')
+
+ def test_decrement_ip(self):
+ self.assertEqual(vyos.template.dec_ip('10.0.0.100/24', '1'), '10.0.0.99')
+ self.assertEqual(vyos.template.dec_ip('10.0.0.90', '10'), '10.0.0.80')
+ self.assertEqual(vyos.template.dec_ip('2001:db8::b/64', '10'), '2001:db8::1')
+ self.assertEqual(vyos.template.dec_ip('2001:db8::f', '5'), '2001:db8::a')
+
+ def test_is_network(self):
+ self.assertFalse(vyos.template.is_ip_network('192.0.2.0'))
+ self.assertFalse(vyos.template.is_ip_network('192.0.2.1/24'))
+ self.assertTrue(vyos.template.is_ip_network('192.0.2.0/24'))
+
+ self.assertFalse(vyos.template.is_ip_network('2001:db8::'))
+ self.assertFalse(vyos.template.is_ip_network('2001:db8::ffff'))
+ self.assertTrue(vyos.template.is_ip_network('2001:db8::/48'))
+ self.assertTrue(vyos.template.is_ip_network('2001:db8:1000::/64'))
+
+ def test_is_network(self):
+ self.assertTrue(vyos.template.compare_netmask('10.0.0.0/8', '20.0.0.0/8'))
+ self.assertTrue(vyos.template.compare_netmask('10.0.0.0/16', '20.0.0.0/16'))
+ self.assertFalse(vyos.template.compare_netmask('10.0.0.0/8', '20.0.0.0/16'))
+ self.assertFalse(vyos.template.compare_netmask('10.0.0.1', '20.0.0.0/16'))
+
+ self.assertTrue(vyos.template.compare_netmask('2001:db8:1000::/48', '2001:db8:2000::/48'))
+ self.assertTrue(vyos.template.compare_netmask('2001:db8:1000::/64', '2001:db8:2000::/64'))
+ self.assertFalse(vyos.template.compare_netmask('2001:db8:1000::/48', '2001:db8:2000::/64'))
+
+ def test_cipher_to_string(self):
+ ESP_DEFAULT = 'aes256gcm128-sha256-ecp256,aes128ccm64-sha256-ecp256'
+ IKEv2_DEFAULT = 'aes256gcm128-sha256-ecp256,aes128ccm128-md5_128-modp1024'
+
+ data = {
+ 'esp_group': {
+ 'ESP_DEFAULT': {
+ 'compression': 'disable',
+ 'lifetime': '3600',
+ 'mode': 'tunnel',
+ 'pfs': 'dh-group19',
+ 'proposal': {
+ '10': {
+ 'encryption': 'aes256gcm128',
+ 'hash': 'sha256',
+ },
+ '20': {
+ 'encryption': 'aes128ccm64',
+ 'hash': 'sha256',
+ }
+ }
+ }
+ },
+ 'ike_group': {
+ 'IKEv2_DEFAULT': {
+ 'close_action': 'none',
+ 'dead_peer_detection': {
+ 'action': 'hold',
+ 'interval': '30',
+ 'timeout': '120'
+ },
+ 'ikev2_reauth': 'no',
+ 'key_exchange': 'ikev2',
+ 'lifetime': '10800',
+ 'mobike': 'disable',
+ 'proposal': {
+ '10': {
+ 'dh_group': '19',
+ 'encryption': 'aes256gcm128',
+ 'hash': 'sha256'
+ },
+ '20': {
+ 'dh_group': '2',
+ 'encryption': 'aes128ccm128',
+ 'hash': 'md5_128'
+ },
+ }
+ }
+ },
+ }
+
+ for group_name, group_config in data['esp_group'].items():
+ ciphers = vyos.template.get_esp_ike_cipher(group_config)
+ self.assertIn(ESP_DEFAULT, ','.join(ciphers))
+
+ for group_name, group_config in data['ike_group'].items():
+ ciphers = vyos.template.get_esp_ike_cipher(group_config)
+ self.assertIn(IKEv2_DEFAULT, ','.join(ciphers))
diff --git a/src/tests/test_utils.py b/src/tests/test_utils.py
new file mode 100644
index 0000000..9ae329c
--- /dev/null
+++ b/src/tests/test_utils.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020-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 unittest import TestCase
+class TestVyOSUtils(TestCase):
+ def test_key_mangling(self):
+ from vyos.utils.dict import mangle_dict_keys
+ data = {"foo-bar": {"baz-quux": None}}
+ expected_data = {"foo_bar": {"baz_quux": None}}
+ new_data = mangle_dict_keys(data, '-', '_')
+ self.assertEqual(new_data, expected_data)
+
+ def test_sysctl_read(self):
+ from vyos.utils.system import sysctl_read
+ self.assertEqual(sysctl_read('net.ipv4.conf.lo.forwarding'), '1')
diff --git a/src/tests/test_utils_network.py b/src/tests/test_utils_network.py
new file mode 100644
index 0000000..5a6dc25
--- /dev/null
+++ b/src/tests/test_utils_network.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020-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/>.
+
+import vyos.utils.network
+from unittest import TestCase
+
+class TestVyOSUtilsNetwork(TestCase):
+ def setUp(self):
+ pass
+
+ def test_is_addr_assigned(self):
+ self.assertTrue(vyos.utils.network.is_addr_assigned('127.0.0.1'))
+ self.assertTrue(vyos.utils.network.is_addr_assigned('::1'))
+ self.assertFalse(vyos.utils.network.is_addr_assigned('127.251.255.123'))
+
+ def test_is_ipv6_link_local(self):
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('169.254.0.1'))
+ self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::'))
+ self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::affe:1'))
+ self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::affe:1%eth0'))
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('2001:db8::'))
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('2001:db8::%eth0'))
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('VyOS'))
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('::1'))
+ self.assertFalse(vyos.utils.network.is_ipv6_link_local('::1%lo'))
+
+ def test_is_ipv6_link_local(self):
+ self.assertTrue(vyos.utils.network.is_loopback_addr('127.0.0.1'))
+ self.assertTrue(vyos.utils.network.is_loopback_addr('127.0.1.1'))
+ self.assertTrue(vyos.utils.network.is_loopback_addr('127.1.1.1'))
+ self.assertTrue(vyos.utils.network.is_loopback_addr('::1'))
+
+ self.assertFalse(vyos.utils.network.is_loopback_addr('::2'))
+ self.assertFalse(vyos.utils.network.is_loopback_addr('192.0.2.1'))
+
+
+