Эх сурвалжийг харах

Add container upgrade script

Mikael Magnusson 5 жил өмнө
parent
commit
8df81af1ed
1 өөрчлөгдсөн 198 нэмэгдсэн , 0 устгасан
  1. 198 0
      scripts/upgrade.py

+ 198 - 0
scripts/upgrade.py

@@ -0,0 +1,198 @@
+#!/usr/bin/python3
+
+import sys
+import time
+import pylxd
+from pylxd import Client
+
+class ExecuteError(RuntimeError):
+        def __init__(self, command, exit_code):
+                self.command = command
+                self.exit_code = exit_code
+
+        def __str__(self):
+                return 'Command "%s" exit code %d' % (' '.join(self.command), self.exit_code)
+
+def find_source_image(client, image):
+        try:
+                client.images.get_by_alias(image)
+                return {'type': 'image', 'alias': image}
+        except pylxd.exceptions.NotFound as e:
+                if len(image) >= 12:
+                        client.images.get(image)
+                        return {'type': 'image', 'fingerprint': image}
+                else:
+                        raise
+
+def copy_config(old, new):
+        new.devices = old.devices
+        new.description = old.description
+        new.profiles = old.profiles
+        new_config = new.config
+
+        for key, value in old.config.items():
+                if key.endswith("hwaddr"):
+                        new_config[key] = value
+
+#        for key, value in new_config.items():
+#                print("%s %s" % (key, value))
+
+        new.config = new_config
+
+def log_stdout(message):
+        print(message, end='', flush=True)
+
+def log_stderr(message):
+        print(message, end='', file=sys.stderr, flush=True)
+
+class Container:
+        def __init__(self, container):
+                self.container = container
+
+        def __getattr__(self, name):
+                return self.container.__getattribute__(name)
+
+        def __setattr__(self, name, value):
+                if name not in ('container'):
+                        self.container.__setattr__(name, value)
+                else:
+                        super(Container, self).__setattr__(name, value)
+
+        def execute_with_output(self, command, *args, **kwargs):
+                extra_args={}
+                if 'stderr_handler' not in kwargs:
+                        extra_args['stderr_handler'] = log_stderr
+                (exit_code, stdout, stderr) = self.container.execute(command, *args, **kwargs, **extra_args)
+                if exit_code != 0:
+                        raise ExecuteError(command, exit_code)
+                return stdout
+
+        def execute(self, command, *args, **kwargs):
+                extra_args={}
+                if 'stdout_handler' not in kwargs:
+                        extra_args['stdout_handler'] = log_stdout
+                self.execute_with_output(command, *args, **kwargs, **extra_args)
+
+        def execute_retry(self, command, retries, *args, **kwargs):
+                for i in range(retries + 1):
+                        try:
+                                self.execute(command, *args, **kwargs)
+                        except ExecuteError as e:
+                                if i == retries:
+                                        raise
+                                continue
+                        return
+                raise NotImplementedError()
+
+        def ping(self, dest):
+                self.execute_retry(['ping', '-c', '1', '-q', dest], 2,
+                                   stdout_handler=None)
+
+        def sysupgrade_backup(self, ):
+                return self.execute_with_output(['sysupgrade', '-b', '-'],
+                                                decode=False)
+
+        def sysupgrade_restore(self, data):
+                self.execute(['sysupgrade', '-r', '-'],
+                        stdin_payload=data, decode=False)
+
+        def opkg_list_installed(self, ):
+                return self.execute_with_output(['opkg', 'list-installed'])
+
+        def opkg_update(self):
+                print("Update")
+                self.execute(['opkg', 'update'])
+
+        def opkg_install(self, packages):
+                print("Installing %s" % packages)
+                self.execute(['opkg', 'install'] + packages)
+
+        def package_set(self):
+                old_list = self.opkg_list_installed().split('\n')
+                old_packages = []
+                i = 1
+                for l in old_list:
+                        i = i + 1
+                        res = l.split(' ')
+                        if len(res) == 3:
+                                (name, _, version) = res
+                                old_packages.append(name)
+                return frozenset(old_packages)
+
+def usage(argv):
+        print("Usage:", argv[0], "<old container> <new container> <image>")
+        exit(1)
+
+def main(argv):
+        is_allow_existing = False
+
+        if len(argv) == 4:
+                pos = 1
+        else:
+                usage(argv)
+
+        old_name = argv[pos]; pos=pos+1
+        new_name = argv[pos]; pos=pos+1
+        new_image = argv[pos]; pos=pos+1
+        client = Client()
+
+        old = Container(client.containers.get(old_name))
+
+        if old.status == 'Stopped':
+                print("Start", old_name)
+                old.start(wait=True)
+
+        new_source = find_source_image(client, new_image)
+        new_config = {'name': new_name, 'source': new_source}
+
+        if is_allow_existing and client.containers.exists(new_name):
+                new = Container(client.containers.get(new_name))
+        else:
+                print("Create", new_name, new_config)
+                new = Container(client.containers.create(new_config, wait=True))
+
+        if new.status == 'Stopped':
+                print("Start", new_name)
+                new.start(wait=True)
+
+                print("Ping downloads.openwrt.org")
+                new.ping('downloads.openwrt.org')
+
+                print("Update package list")
+                new.opkg_update()
+
+        old_set = old.package_set()
+        new_set = new.package_set()
+        add_packages = list(old_set.difference(new_set).difference(['iw']))
+
+        if len(add_packages) > 0:
+                print("Install", add_packages)
+                new.opkg_install(add_packages)
+        else:
+                print("No packages installed")
+
+        print("Backup", old_name)
+        backup_data = old.sysupgrade_backup()
+
+        print("Restore", new_name)
+        new.sysupgrade_restore(backup_data)
+
+        print("Stop", old_name)
+        old.stop(wait=True)
+
+        print("Stop", new_name)
+        new.stop(wait=True)
+
+        print("Copy config")
+        copy_config(old, new)
+        new.save()
+
+        print("Wait 2s")
+        time.sleep(2)
+
+        print("Start", new_name)
+        new.start(wait=True)
+        print("Finished")
+
+if __name__ == '__main__':
+        main(sys.argv)