''' All KVM technology specefic functions are implemented inside this module Two entites belong to this module: a Kvm hypervisor or a Kvm virtual machine ''' from libvirtwrapper import * from exceptions import * import subprocess import hashlib class KvmHypervisor(LibvirtHypervisor): ''' Base class of a Kvm Hypervisor This class have a attribute hv_type for tagging purposes ''' _instance = None def __init__(self): super(KvmHypervisor, self).__init__('kvm') self.hv_type = 'KVM/QEMU' def __new__(cls, *args, **kwargs): ''' .. note:: We use singleton design pattern to force only a single instance of ourlibvirt hypervisor handle, it's essential since we connect with libvirt only on localhost so we must assure one single connection to the hypervisor ''' if cls._instance is None: cls._instance = super(KvmHypervisor, cls).__new__(cls, *args, **kwargs) return cls._instance def start_vm(self, name, start_options=DEFAULT_VM_START): ''' Starts the vm identified by name :param name: The name of the virtual machine :type nane: :class:`str` :param start_options: Options flags while starting vm :type start_options: TODO reference to constants ''' for vm in self._vm_list: if vm.get_name() == name: vm.start() return raise VMError('Virtual Machine %s not found: '% name) def stop_vm(self, name, force=False): ''' Poweroff the specifed vm with the specified options :param name: the name of the vm :type name: :class:`str` ''' for vm in self._vm_list: if vm.get_name() == name: vm.force_poweroff() if force else vm.shutdown() return raise VMError('Virtual Machine %s not found: ' % name) def suspend_vm(self, name): ''' Suspends the specifed vm :param name: the name of the vm :type name: :class:`str` ''' for vm in self._vm_list: if vm.get_name() == name: vm.suspend() return raise VMError('Virtual machine %s not found: ' % name) def resume_vm(self, name): ''' Resumes the specifed vm :param name: the name of the vm :type name: :class:`str` ''' for vm in self._vm_list: if vm.get_name() == name: vm.resume() return raise VMError('Virtual machine %s not found: ' % name) def local_execute(self, command): ''' Excecutes the command given in command on the local host Returns a tuple with (stdout, stderr) from the process that has been executed ..warning:: This is a dangerous function as it gives the command given in paramter to a shell prompt, anything can then be executed locally ..warning:: If the command given is a long processing one, this may be a deadlock to the node be carefull ! :param command: the command to execute with it's arguments :type command: :class:`str` ''' #FIXME: stop using shell=true and parse arguments with shlex.split() p = subprocess.Popen(command, shell=True, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) result = p.communicate() return result def export_vm(self, name, target, port): ''' Migrates the given vm (name) to (target) hypervisor which is already waiting for the migration :param name: the name of the vm :type name: :class:`str` :param target: the destination hypervisor :type target: :class:`str` :param port: the port on the destination to use for the transfer :type port: :class:`str` ''' # We will be using subprocess to pipe the volume using dd in a netcat # connection to the destination # We send a hash of the volume before so that the target can checksum # and validate integrity. # This algorithm is just a proof of concept of the cold migration # process it is likely that it does not anticipates some problems buff = 1024 * 1024 * 10 #10 Mo path = [vm.get_disk_path() for vm in self._vm_list if vm.get_name() == name].pop() dd_cmd = ['dd', 'if=%s' % path] dd_process = subprocess.Popen(dd_cmd, bufsize=-1, stdout=subprocess.PIPE) nc_process = subprocess.Popen(['nc', target, port], bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) #We send dd output to nc dd_out = dd_process.stdout nc_in = nc_process.stdin m = hashlib.md5() print 'going to pipe loop' try: read = dd_out.read(buff) except Exception as err: raise IOError('error while reading dd process output %s' % err) try: while len(read) != 0: print len(read) m.update(read) nc_in.write(read) nc_in.flush() read = dd_out.read(buff) except Exception as err: raise IOError('Error occured while writing to nc process %s' % err) nc_in.close() print m.hexdigest() def get_network_conf(self): raise NotImplementedError def get_storage_conf(self): raise NotImplementedError def get_storage_stats(self): raise NotImplementedError