* Fix issue that causes a new image store to be created at every start of ucloud-api. * VM Migration API call now takes hostname instead of host key. * StorageHandler Classes are introduced. They transparently handles things related to importing of image, make vm out of image, resize vm image, delete vm image etc. * Loggers added to __init__.py of every ucloud component's subpackage. * Non-Trivial Timeout Events are no longer logged. * Fix issue that prevents removal of stopped VMs (i.e VMs that are successfully migrated). * Improved unit handling added. e.g MB, Mb, mB, mb are all Mega Bytes. * VM migration is now possible on IPv6 host. * Destination VM (receiving side of migration of a vm) now correctly expects incoming data on free ephemeral port. * Traceback is no longer output to screen, instead it goes to log file. * All sanity checks are put into a single file. These checks are run by ucloud.py before running any of ucloud component.
		
			
				
	
	
		
			158 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import shutil
 | 
						|
import subprocess as sp
 | 
						|
import os
 | 
						|
import stat
 | 
						|
 | 
						|
from abc import ABC
 | 
						|
from host import logger
 | 
						|
from os.path import join as join_path
 | 
						|
 | 
						|
 | 
						|
class ImageStorageHandler(ABC):
 | 
						|
    def __init__(self, image_base, vm_base):
 | 
						|
        self.image_base = image_base
 | 
						|
        self.vm_base = vm_base
 | 
						|
 | 
						|
    def import_image(self, image_src, image_dest, protect=False):
 | 
						|
        """Put an image at the destination
 | 
						|
        :param src: An Image file
 | 
						|
        :param dest: A path where :param src: is to be put.
 | 
						|
        :param protect: If protect is true then the dest is protect (readonly etc)
 | 
						|
        The obj must exist on filesystem.
 | 
						|
        """
 | 
						|
 | 
						|
        raise NotImplementedError()
 | 
						|
 | 
						|
    def make_vm_image(self, image_path, path):
 | 
						|
        """Copy image from src to dest
 | 
						|
 | 
						|
        :param src: A path
 | 
						|
        :param dest: A path
 | 
						|
 | 
						|
        src and destination must be on same storage system i.e both on file system or both on CEPH etc.
 | 
						|
        """
 | 
						|
        raise NotImplementedError()
 | 
						|
 | 
						|
    def resize_vm_image(self, path, size):
 | 
						|
        """Resize image located at :param path:
 | 
						|
        :param path: The file which is to be resized
 | 
						|
        :param size: Size must be in Megabytes
 | 
						|
        """
 | 
						|
        raise NotImplementedError()
 | 
						|
 | 
						|
    def delete_vm_image(self, path):
 | 
						|
        raise NotImplementedError()
 | 
						|
 | 
						|
    def execute_command(self, command, report=True):
 | 
						|
        command = list(map(str, command))
 | 
						|
        try:
 | 
						|
            output = sp.check_output(command, stderr=sp.PIPE)
 | 
						|
        except Exception as e:
 | 
						|
            if report:
 | 
						|
                print(e)
 | 
						|
                logger.exception(e)
 | 
						|
            return False
 | 
						|
        return True
 | 
						|
 | 
						|
    def vm_path_string(self, path):
 | 
						|
        raise NotImplementedError()
 | 
						|
 | 
						|
    def qemu_path_string(self, path):
 | 
						|
        raise NotImplementedError()
 | 
						|
 | 
						|
    def is_vm_image_exists(self, path):
 | 
						|
        raise NotImplementedError()
 | 
						|
 | 
						|
 | 
						|
class FileSystemBasedImageStorageHandler(ImageStorageHandler):
 | 
						|
    def import_image(self, src, dest, protect=False):
 | 
						|
        dest = join_path(self.image_base, dest)
 | 
						|
        try:
 | 
						|
            shutil.copy(src, dest)
 | 
						|
            if protect:
 | 
						|
                os.chmod(dest, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
 | 
						|
        except Exception as e:
 | 
						|
            logger.exception(e)
 | 
						|
            return False
 | 
						|
        return True
 | 
						|
 | 
						|
    def make_vm_image(self, src, dest):
 | 
						|
        src = join_path(self.image_base, src)
 | 
						|
        dest = join_path(self.vm_base, dest)
 | 
						|
        try:
 | 
						|
            shutil.copy(src, dest)
 | 
						|
        except Exception as e:
 | 
						|
            logger.exception(e)
 | 
						|
            return False
 | 
						|
        return True
 | 
						|
 | 
						|
    def resize_vm_image(self, path, size):
 | 
						|
        path = join_path(self.vm_base, path)
 | 
						|
        command = ["qemu-img", "resize", "-f", "raw", path, "{}M".format(size)]
 | 
						|
        if self.execute_command(command):
 | 
						|
            return True
 | 
						|
        else:
 | 
						|
            self.delete_vm_image(path)
 | 
						|
            return False
 | 
						|
 | 
						|
    def delete_vm_image(self, path):
 | 
						|
        path = join_path(self.vm_base, path)
 | 
						|
        try:
 | 
						|
            os.remove(path)
 | 
						|
        except Exception as e:
 | 
						|
            logger.exception(e)
 | 
						|
            return False
 | 
						|
        return True
 | 
						|
 | 
						|
    def vm_path_string(self, path):
 | 
						|
        return join_path(self.vm_base, path)
 | 
						|
 | 
						|
    def qemu_path_string(self, path):
 | 
						|
        return self.vm_path_string(path)
 | 
						|
 | 
						|
    def is_vm_image_exists(self, path):
 | 
						|
        path = join_path(self.vm_base, path)
 | 
						|
        command = ["ls", path]
 | 
						|
        return self.execute_command(command, report=False)
 | 
						|
 | 
						|
 | 
						|
class CEPHBasedImageStorageHandler(ImageStorageHandler):
 | 
						|
    def import_image(self, src, dest, protect=False):
 | 
						|
        dest = join_path(self.image_base, dest)
 | 
						|
        command = ["rbd", "import", src, dest]
 | 
						|
        if protect:
 | 
						|
            snap_create_command = ["rbd", "snap", "create", "{}@protected".format(dest)]
 | 
						|
            snap_protect_command = ["rbd", "snap", "protect", "{}@protected".format(dest)]
 | 
						|
 | 
						|
            return self.execute_command(command) and self.execute_command(snap_create_command) and\
 | 
						|
                self.execute_command(snap_protect_command)
 | 
						|
 | 
						|
        return self.execute_command(command)
 | 
						|
 | 
						|
    def make_vm_image(self, src, dest):
 | 
						|
        src = join_path(self.image_base, src)
 | 
						|
        dest = join_path(self.vm_base, dest)
 | 
						|
 | 
						|
        command = ["rbd", "clone", "{}@protected".format(src), dest]
 | 
						|
        return self.execute_command(command)
 | 
						|
 | 
						|
    def resize_vm_image(self, path, size):
 | 
						|
        path = join_path(self.vm_base, path)
 | 
						|
        command = ["rbd", "resize", path, "--size", size]
 | 
						|
        return self.execute_command(command)
 | 
						|
 | 
						|
    def delete_vm_image(self, path):
 | 
						|
        path = join_path(self.vm_base, path)
 | 
						|
        command = ["rbd", "rm", path]
 | 
						|
        return self.execute_command(command)
 | 
						|
 | 
						|
    def vm_path_string(self, path):
 | 
						|
        return join_path(self.vm_base, path)
 | 
						|
 | 
						|
    def qemu_path_string(self, path):
 | 
						|
        return "rbd:{}".format(self.vm_path_string(path))
 | 
						|
 | 
						|
    def is_vm_image_exists(self, path):
 | 
						|
        path = join_path(self.vm_base, path)
 | 
						|
        command = ["rbd", "info", path]
 | 
						|
        return self.execute_command(command, report=False)
 |