forked from uncloud/uncloud
meow
cc0ca68498
* 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)
|