diff options
| author | John Estabrook <jestabro@vyos.io> | 2022-03-08 11:46:18 -0600 | 
|---|---|---|
| committer | John Estabrook <jestabro@vyos.io> | 2022-03-08 11:46:18 -0600 | 
| commit | 534f677d36285863decb2cdff179687b4fd690cb (patch) | |
| tree | b79abe885d3b5392f8b2fa434cd4816833eea581 | |
| parent | f8b7846ba3e008aaf0cde77e00e60d9af7b61446 (diff) | |
| download | vyos-1x-534f677d36285863decb2cdff179687b4fd690cb.tar.gz vyos-1x-534f677d36285863decb2cdff179687b4fd690cb.zip | |
component_version: T4291: consolidate read/write functions
| -rw-r--r-- | python/vyos/component_version.py | 174 | ||||
| -rw-r--r-- | python/vyos/component_versions.py | 57 | ||||
| -rw-r--r-- | python/vyos/formatversions.py | 109 | ||||
| -rw-r--r-- | python/vyos/migrator.py | 26 | ||||
| -rw-r--r-- | python/vyos/systemversions.py | 46 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_component_version.py | 6 | ||||
| -rwxr-xr-x | src/helpers/system-versions-foot.py | 19 | 
7 files changed, 189 insertions, 248 deletions
| diff --git a/python/vyos/component_version.py b/python/vyos/component_version.py new file mode 100644 index 000000000..b1554828d --- /dev/null +++ b/python/vyos/component_version.py @@ -0,0 +1,174 @@ +# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io> +# +# 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 <http://www.gnu.org/licenses/>. + +""" +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 deleted file mode 100644 index 90b458aae..000000000 --- a/python/vyos/component_versions.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2017 VyOS maintainers and contributors <maintainers@vyos.io> -# -# 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 <http://www.gnu.org/licenses/>. - -""" -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 deleted file mode 100644 index 29117a5d3..000000000 --- a/python/vyos/formatversions.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> -# -# 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 <http://www.gnu.org/licenses/>. - -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 a2e0daabd..266a2e58e 100644 --- a/python/vyos/migrator.py +++ b/python/vyos/migrator.py @@ -17,10 +17,8 @@ import sys  import os  import json  import subprocess -import vyos.version  import vyos.defaults -import vyos.systemversions as systemversions -import vyos.formatversions as formatversions +import vyos.component_version as component_version  class MigratorError(Exception):      pass @@ -42,13 +40,13 @@ class Migrator(object):          cfg_file = self._config_file          component_versions = {} -        cfg_versions = formatversions.read_vyatta_versions(cfg_file) +        cfg_versions = component_version.from_file(cfg_file, vintage='vyatta')          if cfg_versions:              self._config_file_vintage = 'vyatta'              component_versions = cfg_versions -        cfg_versions = formatversions.read_vyos_versions(cfg_file) +        cfg_versions = component_version.from_file(cfg_file, vintage='vyos')          if cfg_versions:              self._config_file_vintage = 'vyos' @@ -152,19 +150,11 @@ 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': -            formatversions.write_vyatta_versions_foot(self._config_file, -                                                      versions_string, -                                                      os_version_string) +            component_version.write_footer(self._config_file, vintage='vyatta')          if self._config_file_vintage == 'vyos': -            formatversions.write_vyos_versions_foot(self._config_file, -                                                    versions_string, -                                                    os_version_string) +            component_version.write_footer(self._config_file, vintage='vyos')      def save_json_record(self, component_versions: dict):          """ @@ -195,7 +185,7 @@ class Migrator(object):              # This will force calling all migration scripts:              cfg_versions = {} -        sys_versions = systemversions.get_system_component_version() +        sys_versions = component_version.from_system()          # save system component versions in json file for easy reference          self.save_json_record(sys_versions) @@ -211,7 +201,7 @@ class Migrator(object):          if not self._changed:              return -        formatversions.remove_versions(cfg_file) +        component_version.remove_footer(cfg_file)          self.write_config_file_versions(rev_versions) @@ -232,7 +222,7 @@ class VirtualMigrator(Migrator):          if not self._changed:              return -        formatversions.remove_versions(cfg_file) +        component_version.remove_footer(cfg_file)          self.write_config_file_versions(cfg_versions) diff --git a/python/vyos/systemversions.py b/python/vyos/systemversions.py deleted file mode 100644 index f2da76d4f..000000000 --- a/python/vyos/systemversions.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> -# -# 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 <http://www.gnu.org/licenses/>. - -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 777379bdd..21cc1c761 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.systemversions import get_system_versions, get_system_component_version +from vyos.component_version import legacy_from_system, from_system  # After T3474, component versions should be updated in the files in  # vyos-1x/interface-definitions/include/version/ @@ -24,8 +24,8 @@ from vyos.systemversions import get_system_versions, get_system_component_versio  # that in the xml cache.  class TestComponentVersion(unittest.TestCase):      def setUp(self): -        self.legacy_d = get_system_versions() -        self.xml_d = get_system_component_version() +        self.legacy_d = legacy_from_system() +        self.xml_d = from_system()      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 2aa687221..b44408542 100755 --- a/src/helpers/system-versions-foot.py +++ b/src/helpers/system-versions-foot.py @@ -16,24 +16,13 @@  # along with this library.  If not, see <http://www.gnu.org/licenses/>.  import sys -import vyos.formatversions as formatversions -import vyos.systemversions as systemversions  import vyos.defaults -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() +from vyos.component_version import write_footer  sys.stdout.write("\n\n")  if vyos.defaults.cfg_vintage == 'vyos': -    formatversions.write_vyos_versions_foot(None, component_string, -                                            os_version_string) +    write_footer(None, vintage='vyos')  elif vyos.defaults.cfg_vintage == 'vyatta': -    formatversions.write_vyatta_versions_foot(None, component_string, -                                              os_version_string) +    write_footer(None, vintage='vyatta')  else: -    formatversions.write_vyatta_versions_foot(None, component_string, -                                              os_version_string) +    write_footer(None, vintage='vyatta') | 
