def __init__(self, connection): self._wait_timeout = 30 self._connection = ConnectionManager(connection) self._domain = DomainManager(connection) super(LibVirtMachineManager, self).__init__()
def test_delete(self): return # TODO: test delete is incompatible with test driver. # we simply pass this test and add a workaround later with self.assertRaises(DomainManagerError): with DomainManager(self.uri) as dm: self.assertIsInstance(dm, DomainManager) delete = dm.delete('fv0') with DomainManager(self.uri) as dm: stop = dm.stop('fv0') delete = dm.delete('fv0') self.assertIsTrue(delete)
def test_state_with_name(self): with DomainManager(self.uri) as dm: self.assertIsInstance(dm, DomainManager) state = dm.state('4dea22b31d52d8f32516782e98ab3fa0') self.assertIsInstance(state, tuple) self.assertIsNotNone(state[0]) self.assertIsNotNone(state[1])
def test_list(self): with DomainManager(self.uri) as dm: self.assertIsInstance(dm, DomainManager) machines = dm.list() self.assertIsInstance(machines, tuple) self.assertEqual(len(machines), 2) self.assertTrue('fv0' in machines) self.assertTrue('fc4' in machines)
def test_stop_with_name(self): with DomainManager(self.uri) as dm: self.assertIsInstance(dm, DomainManager) # start test machines dm.start('4dea22b31d52d8f32516782e98ab3fa0') # perform tests stop = dm.stop('4dea22b31d52d8f32516782e98ab3fa0') self.assertTrue(stop) stop = dm.stop('4dea22b31d52d8f32516782e98ab3fa0') self.assertFalse(stop)
def test_clone(self): return # TODO: test clone needs specific libvirt configuration # we simply pass this test and add a workaround later with DomainManager(self.uri) as dm: self.assertIsInstance(dm, DomainManager) # clone a started machine clone = dm.clone('fv0', 'fv1') self.assertIsNone(clone) # clone a stopped machine dm.stop('fv0') clone = dm.clone('fv0', 'fv1')
def test_start_with_dict(self): with DomainManager(self.uri) as dm: self.assertIsInstance(dm, DomainManager) # stop test machines stop = dm.stop('4dea22b31d52d8f32516782e98ab3fa0') # perform tests start = dm.start({ 'domain': '4dea22b31d52d8f32516782e98ab3fa0', 'flags': 0 }) self.assertTrue(start) start = dm.start('4dea22b31d52d8f32516782e98ab3fa0') self.assertFalse(start)
def test_lookup_with_name(self): with DomainManager(self.uri) as dm: # query by name self.assertIsInstance(dm, DomainManager) fv0 = dm.lookup('fv0') self.assertIsInstance(fv0, libvirt.virDomain) fc4 = dm.lookup('fc4') self.assertIsInstance(fc4, libvirt.virDomain) # query by name with a list fv0, fc4, dummy = dm.lookup(['fv0', 'fc4', 'dummy']) self.assertIsInstance(fv0, libvirt.virDomain) self.assertIsInstance(fc4, libvirt.virDomain) self.assertIsNone(dummy)
def test_state_with_list(self): with DomainManager(self.uri) as dm: self.assertIsInstance(dm, DomainManager) state = dm.state([ '4dea22b31d52d8f32516782e98ab3fa0', '4e57708ad8a14031b86c2822c4d006a3', ]) self.assertIsInstance(state, tuple) self.assertIsInstance(state[0], tuple) self.assertIsNotNone(state[0][0]) self.assertIsNotNone(state[0][1]) self.assertIsInstance(state[1], tuple) self.assertIsNotNone(state[1][0]) self.assertIsNotNone(state[1][1])
def test_lookup_with_uuid(self): with DomainManager(self.uri) as dm: # query by name self.assertIsInstance(dm, DomainManager) fv0 = dm.lookup('4dea22b31d52d8f32516782e98ab3fa0') self.assertIsInstance(fv0, libvirt.virDomain) fc4 = dm.lookup('4E57708AD8A14031B86C2822C4D006A3') self.assertIsInstance(fc4, libvirt.virDomain) # query by name with a list fv0, fc4, dummy = dm.lookup([ '4dea22b31d52d8f32516782e98ab3fa0', # fv0 '4e57708ad8a14031b86c2822c4d006a3', # fc4 'deadbeef1d52d8f32516782e98ab3fbb' ]) self.assertIsInstance(fv0, libvirt.virDomain) self.assertIsInstance(fc4, libvirt.virDomain) self.assertIsNone(dummy)
def test_lookup_with_id(self): with DomainManager(self.uri) as dm: self.assertIsInstance(dm, DomainManager) fv0 = dm.lookup('4dea22b31d52d8f32516782e98ab3fa0') fc4 = dm.lookup('4E57708AD8A14031B86C2822C4D006A3') dm.start(fv0) dm.start(fc4) fv0 = fv0.ID() fc4 = fc4.ID() # query by name with a list fv0, fc4 = dm.lookup([fv0, fc4]) self.assertIsInstance(fv0, libvirt.virDomain) self.assertIsInstance(fc4, libvirt.virDomain) # lookup with digits fv0, fc4 = dm.lookup(['1', '2']) self.assertIsInstance(fv0, libvirt.virDomain) self.assertIsInstance(fc4, libvirt.virDomain)
def test_start_with_list(self): with DomainManager(self.uri) as dm: self.assertIsInstance(dm, DomainManager) # stop test machines fv0, fc4, dummy = dm.stop([ '4dea22b31d52d8f32516782e98ab3fa0', '4e57708ad8a14031b86c2822c4d006a3', 'deadbeef1d52d8f32516782e98ab3fbb', ]) # perform tests fv0, fc4, dummy = dm.start([ '4dea22b31d52d8f32516782e98ab3fa0', '4e57708ad8a14031b86c2822c4d006a3', 'deadbeef1d52d8f32516782e98ab3fbb', ]) self.assertTrue(fv0) self.assertTrue(fc4) self.assertIsNone(dummy)
class LibVirtMachineManager(VirtualMachineManager, ParametricSingleton): """Machine manager based on libvirt""" # ============================ # parametric singleton stuff # ============================ @staticmethod def depends_on(cls, *args, **kwargs): # singleton is based on the uri, extracted from the libvirt handler (handler,) = args[0] if isinstance(handler, basestring): handler = ConnectionManager(handler) if isinstance(handler, ConnectionManager): handler = handler.connection if not isinstance(handler, libvirt.virConnect): what = type(handler) reason = "Invalid type for 'connection' field: {0}".format(what) raise DomainManagerError(reason) try: uri = handler.getURI() except libvirt.libvirtError as e: reason = "unable to get domain uri from connection handle" raise DomainManagerError(reason) return uri # =================================== # Constructor and destructor stuffs # =================================== def __init__(self, connection): self._wait_timeout = 30 self._connection = ConnectionManager(connection) self._domain = DomainManager(connection) super(LibVirtMachineManager, self).__init__() # ======================= # context manager stuff # ======================= def __enter__(self): return self # ================= # Private methods # ================= def _wait(self, label, state, timeout=0): """ wait for a vm status to be set. :param label: virtual machine name. :param state: virtual machine status, accepts many states with a list. :raise IrmaMachineManagerError: if timeout expire or virtual machine """ if isinstance(state, int): state = [state] seconds = 0 current, desc = self._domain.state(label) while current not in state: if timeout and seconds > int(timeout): reason = "status change timeout for '{0}'".format(label) raise IrmaMachineManagerError(reason) time.sleep(1) seconds += 1 current, desc = self._domain.state(label) # ================ # public methods # ================ ACTIVE = VirtualMachineManager.ACTIVE INACTIVE = VirtualMachineManager.INACTIVE def list(self, filter=ACTIVE | INACTIVE): """ List all (running and inactive) virtual machines :return: list of virtual machines names :raise IrmaMachineManagerError: if unable to list machines """ labels = list() try: labels.extend(self._domain.list(filter)) except DomainManagerError as e: raise IrmaMachineManagerError(e) return labels def start(self, label): """ Start a machine :param label: virtual machine name :raise IrmaMachineManagerError: if unable to start virtual machine. """ state, desc = self._domain.state(label) if state != DomainManager.SHUTOFF: reason = "{0} should be off, currently {0} {1}".format(label, desc) raise IrmaMachineManagerError(reason) try: res = self._domain.start(label) self._wait(label, DomainManager.RUNNING, self._wait_timeout) except DomainManagerError as e: raise IrmaMachineManagerError(e) def stop(self, label, force=False): """ Stop a virtual machine :param label: machine name :param force: if True, halt machine immediatly instead of gracefully :raise IrmaMachineManagerError: if unable to stop virtual machine or find it. """ state, desc = self._domain.state(label) if state != DomainManager.RUNNING: reason = "{0} should be running, {1} instead".format(label, desc) raise IrmaMachineManagerError(reason) try: self._domain.stop(label, force) self._wait(label, DomainManager.SHUTOFF, self._wait_timeout) except DomainManagerError as e: raise IrmaMachineManagerError(e) def clone(self, origin, clone, use_backing_file=True): """ Clone a machine :param src_label: source machine name :param dst_label: destination machine name :raise IrmaMachineManagerError: if the machine exists or is currently running """ # TODO: move checking in the lib.virt.core api state, desc = self._domain.state(origin) if state != DomainManager.SHUTOFF: reason = "{0} should be off, {1} instead".format(origin, desc) raise IrmaMachineManagerError(reason) if self._domain.lookup(clone): reason = "clone {0} already exists".format(clone, desc) raise IrmaMachineManagerError(reason) try: orig_dict = self._domain.info(origin) # if we do not want to use backing files, simply clone if not use_backing_file: self._domain.clone(origin, clone) # we want backing files, check for disks else: clone_dict = orig_dict # generate a new uuid while True: uuid = UUID.generate() if not self._domain.lookup(uuid): break # set new name and new uuid clone_dict['name'] = clone clone_dict['uuid'] = uuid # change devices for type, device in clone_dict['devices'].items(): if type == 'interface': interfaces = device if not isinstance(interfaces, list): interfaces = [interfaces] for interface in interfaces: interface['mac']['@address'] = MAC.generate() elif type == 'disk': disks = device if not isinstance(disks, list): disks = [disks] for disk in disks: disk_path = disk['source']['@file'] vman = StorageVolumeManager(self._connection, None) pman = StoragePoolManager(self._connection) volume = vman.lookup(disk_path) vman.pool = pman.lookupByVolume(volume) # TODO: pool is not defined, have to create one volume = vman.info(disk_path) # check if has a backing storage if volume.backingstore is not None: from_disk = orig_dict['name'] disk_ext = volume.target['format']['@type'] to_disk = '.'.join([clone, disk_ext]) new_vol = vman.clone(from_disk, to_disk) disk['source']['@file'] = new_vol.path() # create a backing storage else: backingvol = volume backingvol.key = None # retreive path basedir = backingvol.target['path'] basedir = os.path.dirname(basedir) disk_ext = volume.target['format']['@type'] disk_name = '.'.join([clone, disk_ext]) backingvol.target['path'] = \ os.path.join(basedir, disk_name) backingvol.backingstore = \ {'path': disk_path, 'format': {'@type': disk_ext}} backingvol.name = '.'.join([clone, disk_ext]) new_vol = vman.create(backingvol) disk['source']['@file'] = new_vol.path() self._domain.create(clone_dict) except DomainManagerError as e: raise IrmaMachineManagerError(e) def delete(self, label): """Delete a machine @param label: machine name @raise NotImplementedError: this method is abstract. """ state, desc = self._domain.state(label) if state != DomainManager.SHUTOFF: reason = "{0} should be off, {1} instead".format(label, desc) raise IrmaMachineManagerError(reason) try: # TODO: add the possibility to keep some disk self._domain.delete(label) except DomainManagerError as e: raise IrmaMachineManagerError(e) def import_config(self, ordered_dict): try: self._domain.create(ordered_dict) except DomainManagerError as e: raise IrmaMachineManagerError(e) def export_config(self, label): try: return self._domain.info(label) except DomainManagerError as e: raise IrmaMachineManagerError(e)
class LibVirtMachineManager(VirtualMachineManager, ParametricSingleton): """Machine manager based on libvirt""" # ============================ # parametric singleton stuff # ============================ @staticmethod def depends_on(cls, *args, **kwargs): # singleton is based on the uri, extracted from the libvirt handler (handler, ) = args[0] if isinstance(handler, basestring): handler = ConnectionManager(handler) if isinstance(handler, ConnectionManager): handler = handler.connection if not isinstance(handler, libvirt.virConnect): what = type(handler) reason = "Invalid type for 'connection' field: {0}".format(what) raise DomainManagerError(reason) try: uri = handler.getURI() except libvirt.libvirtError as e: reason = "unable to get domain uri from connection handle" raise DomainManagerError(reason) return uri # =================================== # Constructor and destructor stuffs # =================================== def __init__(self, connection): self._wait_timeout = 30 self._connection = ConnectionManager(connection) self._domain = DomainManager(connection) super(LibVirtMachineManager, self).__init__() # ======================= # context manager stuff # ======================= def __enter__(self): return self # ================= # Private methods # ================= def _wait(self, label, state, timeout=0): """ wait for a vm status to be set. :param label: virtual machine name. :param state: virtual machine status, accepts many states with a list. :raise IrmaMachineManagerError: if timeout expire or virtual machine """ if isinstance(state, int): state = [state] seconds = 0 current, desc = self._domain.state(label) while current not in state: if timeout and seconds > int(timeout): reason = "status change timeout for '{0}'".format(label) raise IrmaMachineManagerError(reason) time.sleep(1) seconds += 1 current, desc = self._domain.state(label) # ================ # public methods # ================ ACTIVE = VirtualMachineManager.ACTIVE INACTIVE = VirtualMachineManager.INACTIVE def list(self, filter=ACTIVE | INACTIVE): """ List all (running and inactive) virtual machines :return: list of virtual machines names :raise IrmaMachineManagerError: if unable to list machines """ labels = list() try: labels.extend(self._domain.list(filter)) except DomainManagerError as e: raise IrmaMachineManagerError(e) return labels def start(self, label): """ Start a machine :param label: virtual machine name :raise IrmaMachineManagerError: if unable to start virtual machine. """ state, desc = self._domain.state(label) if state != DomainManager.SHUTOFF: reason = "{0} should be off, currently {0} {1}".format(label, desc) raise IrmaMachineManagerError(reason) try: res = self._domain.start(label) self._wait(label, DomainManager.RUNNING, self._wait_timeout) except DomainManagerError as e: raise IrmaMachineManagerError(e) def stop(self, label, force=False): """ Stop a virtual machine :param label: machine name :param force: if True, halt machine immediatly instead of gracefully :raise IrmaMachineManagerError: if unable to stop virtual machine or find it. """ state, desc = self._domain.state(label) if state != DomainManager.RUNNING: reason = "{0} should be running, {1} instead".format(label, desc) raise IrmaMachineManagerError(reason) try: self._domain.stop(label, force) self._wait(label, DomainManager.SHUTOFF, self._wait_timeout) except DomainManagerError as e: raise IrmaMachineManagerError(e) def clone(self, origin, clone, use_backing_file=True): """ Clone a machine :param src_label: source machine name :param dst_label: destination machine name :raise IrmaMachineManagerError: if the machine exists or is currently running """ # TODO: move checking in the lib.virt.core api state, desc = self._domain.state(origin) if state != DomainManager.SHUTOFF: reason = "{0} should be off, {1} instead".format(origin, desc) raise IrmaMachineManagerError(reason) if self._domain.lookup(clone): reason = "clone {0} already exists".format(clone, desc) raise IrmaMachineManagerError(reason) try: orig_dict = self._domain.info(origin) # if we do not want to use backing files, simply clone if not use_backing_file: self._domain.clone(origin, clone) # we want backing files, check for disks else: clone_dict = orig_dict # generate a new uuid while True: uuid = UUID.generate() if not self._domain.lookup(uuid): break # set new name and new uuid clone_dict['name'] = clone clone_dict['uuid'] = uuid # change devices for type, device in clone_dict['devices'].items(): if type == 'interface': interfaces = device if not isinstance(interfaces, list): interfaces = [interfaces] for interface in interfaces: interface['mac']['@address'] = MAC.generate() elif type == 'disk': disks = device if not isinstance(disks, list): disks = [disks] for disk in disks: disk_path = disk['source']['@file'] vman = StorageVolumeManager(self._connection, None) pman = StoragePoolManager(self._connection) volume = vman.lookup(disk_path) vman.pool = pman.lookupByVolume(volume) # TODO: pool is not defined, have to create one volume = vman.info(disk_path) # check if has a backing storage if volume.backingstore is not None: from_disk = orig_dict['name'] disk_ext = volume.target['format']['@type'] to_disk = '.'.join([clone, disk_ext]) new_vol = vman.clone(from_disk, to_disk) disk['source']['@file'] = new_vol.path() # create a backing storage else: backingvol = volume backingvol.key = None # retreive path basedir = backingvol.target['path'] basedir = os.path.dirname(basedir) disk_ext = volume.target['format']['@type'] disk_name = '.'.join([clone, disk_ext]) backingvol.target['path'] = \ os.path.join(basedir, disk_name) backingvol.backingstore = \ {'path': disk_path, 'format': {'@type': disk_ext}} backingvol.name = '.'.join([clone, disk_ext]) new_vol = vman.create(backingvol) disk['source']['@file'] = new_vol.path() self._domain.create(clone_dict) except DomainManagerError as e: raise IrmaMachineManagerError(e) def delete(self, label): """Delete a machine @param label: machine name @raise NotImplementedError: this method is abstract. """ state, desc = self._domain.state(label) if state != DomainManager.SHUTOFF: reason = "{0} should be off, {1} instead".format(label, desc) raise IrmaMachineManagerError(reason) try: # TODO: add the possibility to keep some disk self._domain.delete(label) except DomainManagerError as e: raise IrmaMachineManagerError(e) def import_config(self, ordered_dict): try: self._domain.create(ordered_dict) except DomainManagerError as e: raise IrmaMachineManagerError(e) def export_config(self, label): try: return self._domain.info(label) except DomainManagerError as e: raise IrmaMachineManagerError(e)
def test_with(self): with DomainManager(self.uri) as dm: self.assertIsInstance(dm, DomainManager)
def test_constructor_error(self): with self.assertRaises(DomainManagerError): dummy = True dm = DomainManager(dummy)
def test_constructor_with_connection_manager(self): with ConnectionManager(self.uri) as cm: dm = DomainManager(cm) self.assertIsNotNone(dm) self.assertIsInstance(dm, DomainManager)
def test_constructor_with_basestring(self): dm = DomainManager(self.uri) self.assertIsNotNone(dm) self.assertIsInstance(dm, DomainManager)
def test_constructor_with_virconnect(self): connection = libvirt.open(self.uri) dm = DomainManager(connection) self.assertIsNotNone(dm) self.assertIsInstance(dm, DomainManager) connection.close()
def test_info(self): with DomainManager(self.uri) as dm: # query by name self.assertIsInstance(dm, DomainManager) info = dm.info('4dea22b31d52d8f32516782e98ab3fa0') self.assertIsNotNone(info)