Tomáš Pecka | 04eb737 | 2022-06-07 08:03:04 +0200 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | import json |
| 4 | import re |
| 5 | import os |
| 6 | import pathlib |
| 7 | import shutil |
| 8 | import subprocess |
| 9 | import sys |
| 10 | |
| 11 | import pytest |
| 12 | |
| 13 | |
| 14 | SCRIPT_ROOT = pathlib.Path(__file__).resolve().parent |
| 15 | BR2_ROOT = (SCRIPT_ROOT / '../../').resolve() |
| 16 | |
| 17 | INSTALL_SCRIPT_PATH = BR2_ROOT / 'package/czechlight-cfg-fs/czechlight-install-yang.sh' |
| 18 | MIGRATE_SCRIPT_PATH = BR2_ROOT / 'package/czechlight-cfg-fs/czechlight-migrate.sh' |
| 19 | MIGRATE_DEFINITIONS_PATH = BR2_ROOT / 'package/czechlight-cfg-fs/czechlight-migration-list.sh' |
| 20 | NETOPEER_SCRIPT_PATH = BR2_ROOT / 'submodules/buildroot/package/netopeer2/setup.sh' |
| 21 | |
| 22 | CLA_SYSREPO_PATH = BR2_ROOT / 'submodules/cla-sysrepo' |
| 23 | VELIA_PATH = BR2_ROOT / 'submodules/velia' |
| 24 | NETOPEER2_PATH = BR2_ROOT / 'submodules/dependencies/Netopeer2' |
| 25 | |
| 26 | |
| 27 | def run_and_wait(ctx, desc, command_args): |
| 28 | print(f'executing {desc}') |
| 29 | with subprocess.Popen(command_args, stdout=sys.stdout, stderr=sys.stderr, env=ctx.get_env()) as proc: |
| 30 | proc.wait() |
| 31 | assert proc.returncode == 0 |
| 32 | |
| 33 | |
| 34 | class SysrepoFixture: |
| 35 | def __init__(self, test_directory, tmp_path): |
| 36 | test_directory = SCRIPT_ROOT / test_directory |
| 37 | self.test_name = test_directory.name |
| 38 | |
| 39 | self.expected_file = test_directory / 'expected.json' |
| 40 | assert self.expected_file.is_file() |
| 41 | |
| 42 | startup = test_directory / 'startup.json' |
| 43 | assert startup.is_file() |
| 44 | |
| 45 | self.proc_cmdline = test_directory / 'cmdline' |
| 46 | assert self.proc_cmdline.is_file() |
| 47 | |
| 48 | version_file = test_directory / 'version' |
| 49 | assert version_file.is_file() |
| 50 | |
| 51 | tested_xpath_file = test_directory / 'xpath' |
| 52 | self.tested_xpath = tested_xpath_file.read_text() if tested_xpath_file.is_file() else None |
| 53 | |
| 54 | self._running_directory = tmp_path / self.test_name |
| 55 | self._running_directory.mkdir() |
| 56 | |
| 57 | self.startup_file = self._running_directory / 'startup.json' |
| 58 | shutil.copyfile(startup, self.startup_file) |
| 59 | |
| 60 | self.export_file = self._running_directory / 'export.json' |
| 61 | |
| 62 | self.version_file = self._running_directory / 'version' |
| 63 | shutil.copy(version_file, self.version_file) |
| 64 | |
| 65 | def get_env(self): |
| 66 | res = os.environ.copy() |
| 67 | res['SYSREPO_SHM_PREFIX'] = self.test_name |
| 68 | res['SYSREPO_REPOSITORY_PATH'] = self._running_directory / 'sysrepo_repository' |
| 69 | res['CLA_YANG'] = CLA_SYSREPO_PATH / 'yang' |
| 70 | res['VELIA_YANG'] = VELIA_PATH / 'yang' |
| 71 | res['PROC_CMDLINE'] = self.proc_cmdline |
| 72 | res['CFG_VERSION_FILE'] = self.version_file |
| 73 | res['CFG_STARTUP_FILE'] = self.startup_file |
| 74 | res['NP2_MODULE_DIR'] = NETOPEER2_PATH / 'modules' |
| 75 | res['NP2_MODULE_PERMS'] = '0600' |
| 76 | res['USER'] = os.getlogin() |
| 77 | return res |
| 78 | |
| 79 | |
| 80 | @pytest.fixture(scope='session') |
| 81 | def max_version(): |
| 82 | """ |
| 83 | Fetches last version from czechlight-migrate script by sourcing the |
| 84 | migration definitions file and verifying the length of the migration |
| 85 | files array. |
| 86 | """ |
| 87 | args = ["/bin/bash", "-c", "source " + str(MIGRATE_DEFINITIONS_PATH) + " && echo ${#MIGRATION_FILES[@]}"] |
| 88 | with subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc: |
| 89 | proc.wait() |
| 90 | stdout, stderr = proc.communicate() |
| 91 | assert stderr.decode().strip() == '' |
| 92 | assert proc.returncode == 0 |
| 93 | |
| 94 | return int(stdout.decode().strip()) |
| 95 | |
| 96 | |
| 97 | @pytest.fixture |
| 98 | def sysrepo_fixture(request, tmp_path): |
| 99 | return SysrepoFixture(pathlib.Path(SCRIPT_ROOT / 'data' / request.param), tmp_path) |
| 100 | |
| 101 | |
| 102 | def find_test_directories(): |
| 103 | return [pytest.param(dirname) for dirname in os.listdir(path=SCRIPT_ROOT / 'data')] |
| 104 | |
| 105 | |
| 106 | @pytest.mark.parametrize("sysrepo_fixture", find_test_directories(), indirect=True) |
| 107 | def test(sysrepo_fixture, max_version): |
| 108 | # prepare sysrepo |
| 109 | run_and_wait(sysrepo_fixture, 'netopeer2 setup.sh', [NETOPEER_SCRIPT_PATH]) |
| 110 | run_and_wait(sysrepo_fixture, 'czechlight-install-yang.sh', [INSTALL_SCRIPT_PATH]) |
| 111 | run_and_wait(sysrepo_fixture, 'restoring startup.json to sysrepo', ['sysrepocfg', '--datastore', 'startup', '--format', 'json', f'--import={sysrepo_fixture.startup_file}']) |
| 112 | |
| 113 | current_version = int(sysrepo_fixture.version_file.read_text()) |
| 114 | |
| 115 | # perform the actual migration |
| 116 | print(f'migration: current version is {current_version}') |
| 117 | print('migration: applying migration script') |
| 118 | run_and_wait(sysrepo_fixture, 'migration', [MIGRATE_SCRIPT_PATH]) |
| 119 | |
| 120 | after_migration_version = int(sysrepo_fixture.version_file.read_text()) |
| 121 | assert after_migration_version == max_version |
| 122 | |
| 123 | print('migration: checking datastore contents') |
| 124 | export_args = ['sysrepocfg', '--datastore', 'startup', '-f', 'json', f'--export={sysrepo_fixture.export_file}'] |
| 125 | if sysrepo_fixture.tested_xpath: |
| 126 | export_args += ['-x', sysrepo_fixture.tested_xpath] |
| 127 | run_and_wait(sysrepo_fixture, 'export', export_args) |
| 128 | |
| 129 | with open(sysrepo_fixture.export_file, 'r') as fp_actual: |
| 130 | with open(sysrepo_fixture.expected_file, 'r') as fp_expected: |
| 131 | print(f'migration: comparing files {sysrepo_fixture.startup_file.name} and {sysrepo_fixture.expected_file.name}') |
| 132 | |
| 133 | actual = json.load(fp_actual) |
| 134 | expected = json.load(fp_expected) |
| 135 | assert actual == expected |