summaryrefslogtreecommitdiff
path: root/python/vyos/component_version.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/vyos/component_version.py')
-rw-r--r--python/vyos/component_version.py192
1 files changed, 192 insertions, 0 deletions
diff --git a/python/vyos/component_version.py b/python/vyos/component_version.py
new file mode 100644
index 000000000..a4e318d08
--- /dev/null
+++ b/python/vyos/component_version.py
@@ -0,0 +1,192 @@
+# 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
+from vyos.defaults import directories
+
+DEFAULT_CONFIG_PATH = os.path.join(directories['config'], 'config.boot')
+
+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=DEFAULT_CONFIG_PATH, vintage='vyos'):
+ """
+ Get component version dictionary parsing config file line by line
+ """
+ with open(config_file_name, 'r') as f:
+ 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():
+ """
+ Get system component version dict from legacy location.
+ This is for a transitional sanity check; the directory will eventually
+ be removed.
+ """
+ system_versions = {}
+ legacy_dir = directories['current']
+
+ # To be removed:
+ if not os.path.isdir(legacy_dir):
+ return system_versions
+
+ try:
+ version_info = os.listdir(legacy_dir)
+ except OSError as err:
+ sys.exit(repr(err))
+
+ 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 version_footer(ver: dict, vintage='vyos') -> str:
+ """
+ Version footer as string.
+ """
+ ver_str = format_string(ver)
+ 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 system_footer(vintage='vyos') -> str:
+ """
+ System version footer as string.
+ """
+ ver_d = from_system()
+ return version_footer(ver_d, vintage=vintage)
+
+def write_version_footer(ver: dict, file_name, vintage='vyos'):
+ """
+ Write version footer to file.
+ """
+ footer = version_footer(ver=ver, vintage=vintage)
+ if file_name:
+ with open(file_name, 'a') as f:
+ f.write(footer)
+ else:
+ sys.stdout.write(footer)
+
+def write_system_footer(file_name, vintage='vyos'):
+ """
+ Write system version footer to file.
+ """
+ ver_d = from_system()
+ return write_version_footer(ver_d, file_name=file_name, vintage=vintage)
+
+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)