From c1e04cea0817db07b22a8bd8e6b2f2c0a1e682f4 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Thu, 10 Mar 2022 10:07:31 -0600 Subject: Revert "component_version: T4291: consolidate read/write functions" This reverts commit 534f677d36285863decb2cdff179687b4fd690cb. Revert while investigating failure in vyos-configtest. --- python/vyos/component_version.py | 174 ------------------------ python/vyos/component_versions.py | 57 ++++++++ python/vyos/formatversions.py | 109 +++++++++++++++ python/vyos/migrator.py | 26 ++-- python/vyos/systemversions.py | 46 +++++++ smoketest/scripts/cli/test_component_version.py | 6 +- src/helpers/system-versions-foot.py | 19 ++- 7 files changed, 248 insertions(+), 189 deletions(-) delete mode 100644 python/vyos/component_version.py create mode 100644 python/vyos/component_versions.py create mode 100644 python/vyos/formatversions.py create mode 100644 python/vyos/systemversions.py diff --git a/python/vyos/component_version.py b/python/vyos/component_version.py deleted file mode 100644 index b1554828d..000000000 --- a/python/vyos/component_version.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright 2022 VyOS maintainers and contributors -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see . - -""" -Functions for reading/writing component versions. - -The config file version string has the following form: - -VyOS 1.3/1.4: - -// Warning: Do not remove the following line. -// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@22:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" -// Release version: 1.3.0 - -VyOS 1.2: - -/* Warning: Do not remove the following line. */ -/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pppoe-server@2:pptp@1:qos@1:quagga@7:snmp@1:ssh@1:system@10:vrrp@2:wanloadbalance@3:webgui@1:webproxy@2:zone-policy@1" === */ -/* Release version: 1.2.8 */ - -""" - -import os -import re -import sys -import fileinput -from vyos.xml import component_version -from vyos.version import get_version - -def from_string(string_line, vintage='vyos'): - """ - Get component version dictionary from string. - Return empty dictionary if string contains no config information - or raise error if component version string malformed. - """ - version_dict = {} - - if vintage == 'vyos': - if re.match(r'// vyos-config-version:.+', string_line): - if not re.match(r'// vyos-config-version:\s+"([\w,-]+@\d+:)+([\w,-]+@\d+)"\s*', string_line): - raise ValueError(f"malformed configuration string: {string_line}") - - for pair in re.findall(r'([\w,-]+)@(\d+)', string_line): - version_dict[pair[0]] = int(pair[1]) - - elif vintage == 'vyatta': - if re.match(r'/\* === vyatta-config-version:.+=== \*/$', string_line): - if not re.match(r'/\* === vyatta-config-version:\s+"([\w,-]+@\d+:)+([\w,-]+@\d+)"\s+=== \*/$', string_line): - raise ValueError(f"malformed configuration string: {string_line}") - - for pair in re.findall(r'([\w,-]+)@(\d+)', string_line): - version_dict[pair[0]] = int(pair[1]) - else: - raise ValueError("Unknown config string vintage") - - return version_dict - -def from_file(config_file_name='/opt/vyatta/etc/config/config.boot', - vintage='vyos'): - """ - Get component version dictionary parsing config file line by line - """ - f = open(config_file_name, 'r') - for line_in_config in f: - version_dict = from_string(line_in_config, vintage=vintage) - if version_dict: - return version_dict - - # no version information - return {} - -def from_system(): - """ - Get system component version dict. - """ - return component_version() - -def legacy_from_system(): - """ - legacy function; imported as-is. - - Get component versions from running system; critical failure if - unable to read migration directory. - """ - import vyos.defaults - - system_versions = {} - - try: - version_info = os.listdir(vyos.defaults.directories['current']) - except OSError as err: - print("OS error: {}".format(err)) - sys.exit(1) - - for info in version_info: - if re.match(r'[\w,-]+@\d+', info): - pair = info.split('@') - system_versions[pair[0]] = int(pair[1]) - - return system_versions - -def format_string(ver: dict) -> str: - """ - Version dict to string. - """ - keys = list(ver) - keys.sort() - l = [] - for k in keys: - v = ver[k] - l.append(f'{k}@{v}') - sep = ':' - return sep.join(l) - -def system_footer(vintage='vyos') -> str: - """ - Version footer as string. - """ - ver_str = format_string(from_system()) - release = get_version() - if vintage == 'vyos': - ret_str = (f'// Warning: Do not remove the following line.\n' - + f'// vyos-config-version: "{ver_str}"\n' - + f'// Release version: {release}\n') - elif vintage == 'vyatta': - ret_str = (f'/* Warning: Do not remove the following line. */\n' - + f'/* === vyatta-config-version: "{ver_str}" === */\n' - + f'/* Release version: {release} */\n') - else: - raise ValueError("Unknown config string vintage") - - return ret_str - -def write_footer(file_name, vintage='vyos'): - """ - Write version footer to file. - """ - footer = system_footer(vintage=vintage) - if file_name: - with open(file_name, 'a') as f: - f.write(footer) - else: - sys.stdout.write(footer) - -def remove_footer(file_name): - """ - Remove old version footer. - """ - for line in fileinput.input(file_name, inplace=True): - if re.match(r'/\* Warning:.+ \*/$', line): - continue - if re.match(r'/\* === vyatta-config-version:.+=== \*/$', line): - continue - if re.match(r'/\* Release version:.+ \*/$', line): - continue - if re.match('// vyos-config-version:.+', line): - continue - if re.match('// Warning:.+', line): - continue - if re.match('// Release version:.+', line): - continue - sys.stdout.write(line) diff --git a/python/vyos/component_versions.py b/python/vyos/component_versions.py new file mode 100644 index 000000000..90b458aae --- /dev/null +++ b/python/vyos/component_versions.py @@ -0,0 +1,57 @@ +# Copyright 2017 VyOS maintainers and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see . + +""" +The version data looks like: + +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: +"cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@1:dhcp-server@4:firewall@5:ipsec@4:nat@4:qos@1:quagga@2:system@8:vrrp@1:wanloadbalance@3:webgui@1:webproxy@1:zone-policy@1" +=== */ +/* Release version: 1.2.0-rolling+201806131737 */ +""" + +import re + +def get_component_version(string_line): + """ + Get component version dictionary from string + return empty dictionary if string contains no config information + or raise error if component version string malformed + """ + return_value = {} + if re.match(r'/\* === vyatta-config-version:.+=== \*/$', string_line): + + if not re.match(r'/\* === vyatta-config-version:\s+"([\w,-]+@\d+:)+([\w,-]+@\d+)"\s+=== \*/$', string_line): + raise ValueError("malformed configuration string: " + str(string_line)) + + for pair in re.findall(r'([\w,-]+)@(\d+)', string_line): + if pair[0] in return_value.keys(): + raise ValueError("duplicate unit name: \"" + str(pair[0]) + "\" in string: \"" + string_line + "\"") + return_value[pair[0]] = int(pair[1]) + + return return_value + + +def get_component_versions_from_file(config_file_name='/opt/vyatta/etc/config/config.boot'): + """ + Get component version dictionary parsing config file line by line + """ + f = open(config_file_name, 'r') + for line_in_config in f: + component_version = get_component_version(line_in_config) + if component_version: + return component_version + raise ValueError("no config string in file:", config_file_name) diff --git a/python/vyos/formatversions.py b/python/vyos/formatversions.py new file mode 100644 index 000000000..29117a5d3 --- /dev/null +++ b/python/vyos/formatversions.py @@ -0,0 +1,109 @@ +# Copyright 2019 VyOS maintainers and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see . + +import sys +import os +import re +import fileinput + +def read_vyatta_versions(config_file): + config_file_versions = {} + + with open(config_file, 'r') as config_file_handle: + for config_line in config_file_handle: + if re.match(r'/\* === vyatta-config-version:.+=== \*/$', config_line): + if not re.match(r'/\* === vyatta-config-version:\s+"([\w,-]+@\d+:)+([\w,-]+@\d+)"\s+=== \*/$', config_line): + raise ValueError("malformed configuration string: " + "{}".format(config_line)) + + for pair in re.findall(r'([\w,-]+)@(\d+)', config_line): + config_file_versions[pair[0]] = int(pair[1]) + + + return config_file_versions + +def read_vyos_versions(config_file): + config_file_versions = {} + + with open(config_file, 'r') as config_file_handle: + for config_line in config_file_handle: + if re.match(r'// vyos-config-version:.+', config_line): + if not re.match(r'// vyos-config-version:\s+"([\w,-]+@\d+:)+([\w,-]+@\d+)"\s*', config_line): + raise ValueError("malformed configuration string: " + "{}".format(config_line)) + + for pair in re.findall(r'([\w,-]+)@(\d+)', config_line): + config_file_versions[pair[0]] = int(pair[1]) + + return config_file_versions + +def remove_versions(config_file): + """ + Remove old version string. + """ + for line in fileinput.input(config_file, inplace=True): + if re.match(r'/\* Warning:.+ \*/$', line): + continue + if re.match(r'/\* === vyatta-config-version:.+=== \*/$', line): + continue + if re.match(r'/\* Release version:.+ \*/$', line): + continue + if re.match('// vyos-config-version:.+', line): + continue + if re.match('// Warning:.+', line): + continue + if re.match('// Release version:.+', line): + continue + sys.stdout.write(line) + +def format_versions_string(config_versions): + cfg_keys = list(config_versions.keys()) + cfg_keys.sort() + + component_version_strings = [] + + for key in cfg_keys: + cfg_vers = config_versions[key] + component_version_strings.append('{}@{}'.format(key, cfg_vers)) + + separator = ":" + component_version_string = separator.join(component_version_strings) + + return component_version_string + +def write_vyatta_versions_foot(config_file, component_version_string, + os_version_string): + if config_file: + with open(config_file, 'a') as config_file_handle: + config_file_handle.write('/* Warning: Do not remove the following line. */\n') + config_file_handle.write('/* === vyatta-config-version: "{}" === */\n'.format(component_version_string)) + config_file_handle.write('/* Release version: {} */\n'.format(os_version_string)) + else: + sys.stdout.write('/* Warning: Do not remove the following line. */\n') + sys.stdout.write('/* === vyatta-config-version: "{}" === */\n'.format(component_version_string)) + sys.stdout.write('/* Release version: {} */\n'.format(os_version_string)) + +def write_vyos_versions_foot(config_file, component_version_string, + os_version_string): + if config_file: + with open(config_file, 'a') as config_file_handle: + config_file_handle.write('// Warning: Do not remove the following line.\n') + config_file_handle.write('// vyos-config-version: "{}"\n'.format(component_version_string)) + config_file_handle.write('// Release version: {}\n'.format(os_version_string)) + else: + sys.stdout.write('// Warning: Do not remove the following line.\n') + sys.stdout.write('// vyos-config-version: "{}"\n'.format(component_version_string)) + sys.stdout.write('// Release version: {}\n'.format(os_version_string)) + diff --git a/python/vyos/migrator.py b/python/vyos/migrator.py index 266a2e58e..a2e0daabd 100644 --- a/python/vyos/migrator.py +++ b/python/vyos/migrator.py @@ -17,8 +17,10 @@ import sys import os import json import subprocess +import vyos.version import vyos.defaults -import vyos.component_version as component_version +import vyos.systemversions as systemversions +import vyos.formatversions as formatversions class MigratorError(Exception): pass @@ -40,13 +42,13 @@ class Migrator(object): cfg_file = self._config_file component_versions = {} - cfg_versions = component_version.from_file(cfg_file, vintage='vyatta') + cfg_versions = formatversions.read_vyatta_versions(cfg_file) if cfg_versions: self._config_file_vintage = 'vyatta' component_versions = cfg_versions - cfg_versions = component_version.from_file(cfg_file, vintage='vyos') + cfg_versions = formatversions.read_vyos_versions(cfg_file) if cfg_versions: self._config_file_vintage = 'vyos' @@ -150,11 +152,19 @@ class Migrator(object): """ Write new versions string. """ + versions_string = formatversions.format_versions_string(cfg_versions) + + os_version_string = vyos.version.get_version() + if self._config_file_vintage == 'vyatta': - component_version.write_footer(self._config_file, vintage='vyatta') + formatversions.write_vyatta_versions_foot(self._config_file, + versions_string, + os_version_string) if self._config_file_vintage == 'vyos': - component_version.write_footer(self._config_file, vintage='vyos') + formatversions.write_vyos_versions_foot(self._config_file, + versions_string, + os_version_string) def save_json_record(self, component_versions: dict): """ @@ -185,7 +195,7 @@ class Migrator(object): # This will force calling all migration scripts: cfg_versions = {} - sys_versions = component_version.from_system() + sys_versions = systemversions.get_system_component_version() # save system component versions in json file for easy reference self.save_json_record(sys_versions) @@ -201,7 +211,7 @@ class Migrator(object): if not self._changed: return - component_version.remove_footer(cfg_file) + formatversions.remove_versions(cfg_file) self.write_config_file_versions(rev_versions) @@ -222,7 +232,7 @@ class VirtualMigrator(Migrator): if not self._changed: return - component_version.remove_footer(cfg_file) + formatversions.remove_versions(cfg_file) self.write_config_file_versions(cfg_versions) diff --git a/python/vyos/systemversions.py b/python/vyos/systemversions.py new file mode 100644 index 000000000..f2da76d4f --- /dev/null +++ b/python/vyos/systemversions.py @@ -0,0 +1,46 @@ +# Copyright 2019 VyOS maintainers and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see . + +import os +import re +import sys +import vyos.defaults +from vyos.xml import component_version + +# legacy version, reading from the file names in +# /opt/vyatta/etc/config-migrate/current +def get_system_versions(): + """ + Get component versions from running system; critical failure if + unable to read migration directory. + """ + system_versions = {} + + try: + version_info = os.listdir(vyos.defaults.directories['current']) + except OSError as err: + print("OS error: {}".format(err)) + sys.exit(1) + + for info in version_info: + if re.match(r'[\w,-]+@\d+', info): + pair = info.split('@') + system_versions[pair[0]] = int(pair[1]) + + return system_versions + +# read from xml cache +def get_system_component_version(): + return component_version() diff --git a/smoketest/scripts/cli/test_component_version.py b/smoketest/scripts/cli/test_component_version.py index 21cc1c761..777379bdd 100755 --- a/smoketest/scripts/cli/test_component_version.py +++ b/smoketest/scripts/cli/test_component_version.py @@ -16,7 +16,7 @@ import unittest -from vyos.component_version import legacy_from_system, from_system +from vyos.systemversions import get_system_versions, get_system_component_version # After T3474, component versions should be updated in the files in # vyos-1x/interface-definitions/include/version/ @@ -24,8 +24,8 @@ from vyos.component_version import legacy_from_system, from_system # that in the xml cache. class TestComponentVersion(unittest.TestCase): def setUp(self): - self.legacy_d = legacy_from_system() - self.xml_d = from_system() + self.legacy_d = get_system_versions() + self.xml_d = get_system_component_version() def test_component_version(self): self.assertTrue(set(self.legacy_d).issubset(set(self.xml_d))) diff --git a/src/helpers/system-versions-foot.py b/src/helpers/system-versions-foot.py index b44408542..2aa687221 100755 --- a/src/helpers/system-versions-foot.py +++ b/src/helpers/system-versions-foot.py @@ -16,13 +16,24 @@ # along with this library. If not, see . import sys +import vyos.formatversions as formatversions +import vyos.systemversions as systemversions import vyos.defaults -from vyos.component_version import write_footer +import vyos.version + +sys_versions = systemversions.get_system_component_version() + +component_string = formatversions.format_versions_string(sys_versions) + +os_version_string = vyos.version.get_version() sys.stdout.write("\n\n") if vyos.defaults.cfg_vintage == 'vyos': - write_footer(None, vintage='vyos') + formatversions.write_vyos_versions_foot(None, component_string, + os_version_string) elif vyos.defaults.cfg_vintage == 'vyatta': - write_footer(None, vintage='vyatta') + formatversions.write_vyatta_versions_foot(None, component_string, + os_version_string) else: - write_footer(None, vintage='vyatta') + formatversions.write_vyatta_versions_foot(None, component_string, + os_version_string) -- cgit v1.2.3