for i in range(0, timeout): if model.task_lookup(taskid)['status'] == 'running': time.sleep(1) def test_get_distros(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) distros = inst.distros_get_list() for d in distros: distro = inst.distro_lookup(d) self.assertIn('name', distro) self.assertIn('os_distro', distro) self.assertIn('os_version', distro) self.assertIn('path', distro) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_get_hostinfo(self): inst = model.Model('qemu:///system', objstore_loc=self.tmp_store) info = inst.host_lookup() distro, version, codename = platform.linux_distribution() self.assertIn('cpu', info) self.assertEquals(distro, info['os_distro']) self.assertEquals(version, info['os_version']) self.assertEquals(unicode(codename, "utf-8"), info['os_codename']) self.assertEquals(psutil.TOTAL_PHYMEM, info['memory']) def test_get_hoststats(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) time.sleep(1.5)
class ModelTests(unittest.TestCase): def setUp(self): self.tmp_store = '/tmp/kimchi-store-test' def tearDown(self): os.unlink(self.tmp_store) def test_vm_info(self): inst = model.Model('test:///default', self.tmp_store) vms = inst.vms_get_list() self.assertEquals(1, len(vms)) self.assertEquals('test', vms[0]) keys = set(('state', 'stats', 'uuid', 'memory', 'cpus', 'screenshot', 'icon', 'graphics')) stats_keys = set( ('cpu_utilization', 'net_throughput', 'net_throughput_peak', 'io_throughput', 'io_throughput_peak')) info = inst.vm_lookup('test') self.assertEquals(keys, set(info.keys())) self.assertEquals('running', info['state']) self.assertEquals(2048, info['memory']) self.assertEquals(2, info['cpus']) self.assertEquals(None, info['icon']) self.assertEquals(stats_keys, set(info['stats'].keys())) self.assertRaises(NotFoundError, inst.vm_lookup, 'nosuchvm') @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_lifecycle(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = {'name': 'test', 'disks': []} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': 'kimchi-vm', 'template': '/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'kimchi-vm') vms = inst.vms_get_list() self.assertTrue('kimchi-vm' in vms) inst.vm_start('kimchi-vm') rollback.prependDefer(inst.vm_stop, 'kimchi-vm') info = inst.vm_lookup('kimchi-vm') self.assertEquals('running', info['state']) vms = inst.vms_get_list() self.assertFalse('kimchi-vm' in vms) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_graphics(self): inst = model.Model(objstore_loc=self.tmp_store) params = {'name': 'test', 'disks': []} inst.templates_create(params) with RollbackContext() as rollback: params = {'name': 'kimchi-vnc', 'template': '/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'kimchi-vnc') info = inst.vm_lookup('kimchi-vnc') self.assertEquals('vnc', info['graphics']['type']) self.assertEquals('0.0.0.0', info['graphics']['listen']) graphics = {'type': 'spice', 'listen': '127.0.0.1'} params = { 'name': 'kimchi-spice', 'template': '/templates/test', 'graphics': graphics } inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'kimchi-spice') info = inst.vm_lookup('kimchi-spice') self.assertEquals('spice', info['graphics']['type']) self.assertEquals('127.0.0.1', info['graphics']['listen']) inst.template_delete('test') @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_ifaces(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = {'name': 'test', 'disks': []} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': 'kimchi-ifaces', 'template': '/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'kimchi-ifaces') # Create a network net_name = 'test-network' net_args = { 'name': net_name, 'connection': 'nat', 'subnet': '127.0.100.0/24' } inst.networks_create(net_args) rollback.prependDefer(inst.network_delete, net_name) ifaces = inst.vmifaces_get_list('kimchi-ifaces') self.assertEquals(1, len(ifaces)) iface = inst.vmiface_lookup('kimchi-ifaces', ifaces[0]) self.assertEquals(17, len(iface['mac'])) self.assertEquals("default", iface['network']) self.assertIn("model", iface) # attach network interface to vm iface_args = { "type": "network", "network": "test-network", "model": "virtio" } mac = inst.vmifaces_create('kimchi-ifaces', iface_args) self.assertEquals(17, len(mac)) # detach network interface from vm rollback.prependDefer(inst.vmiface_delete, 'kimchi-ifaces', mac) iface = inst.vmiface_lookup('kimchi-ifaces', mac) self.assertEquals("network", iface["type"]) self.assertEquals("test-network", iface['network']) self.assertEquals("virtio", iface["model"]) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_cdrom(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: vm_name = 'kimchi-cdrom' params = {'name': 'test', 'disks': []} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': vm_name, 'template': '/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, vm_name) prev_count = len(inst.vmstorages_get_list(vm_name)) self.assertEquals(1, prev_count) # dummy .iso files iso_path = '/tmp/existent.iso' iso_path2 = '/tmp/existent2.iso' open(iso_path, 'w').close() open(iso_path2, 'w').close() wrong_iso_path = '/nonexistent.iso' rollback.prependDefer(os.remove, iso_path) rollback.prependDefer(os.remove, iso_path2) # Create a cdrom cdrom_args = {"type": "cdrom", "path": iso_path} cdrom_dev = inst.vmstorages_create(vm_name, cdrom_args) storage_list = inst.vmstorages_get_list(vm_name) self.assertEquals(prev_count + 1, len(storage_list)) # Get cdrom info cd_info = inst.vmstorage_lookup(vm_name, cdrom_dev) self.assertEquals(u'cdrom', cd_info['type']) self.assertEquals(iso_path, cd_info['path']) # create a cdrom with existing dev_name cdrom_args['dev'] = cdrom_dev self.assertRaises(OperationFailed, inst.vmstorages_create, vm_name, cdrom_args) # update path of existing cd with # non existent iso self.assertRaises(InvalidParameter, inst.vmstorage_update, vm_name, cdrom_dev, {'path': wrong_iso_path}) # update path of existing cd with existent iso of shutoff vm inst.vmstorage_update(vm_name, cdrom_dev, {'path': iso_path2}) cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev) self.assertEquals(iso_path2, cdrom_info['path']) # update path of existing cd with existent iso of running vm inst.vm_start(vm_name) inst.vmstorage_update(vm_name, cdrom_dev, {'path': iso_path}) cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev) self.assertEquals(iso_path, cdrom_info['path']) inst.vm_stop(vm_name) # removing non existent cdrom self.assertRaises(NotFoundError, inst.vmstorage_delete, vm_name, "fakedev") # removing valid cdrom inst.vmstorage_delete(vm_name, cdrom_dev) storage_list = inst.vmstorages_get_list(vm_name) self.assertEquals(prev_count, len(storage_list)) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_storage_provisioning(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = {'name': 'test', 'disks': [{'size': 1}]} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': 'test-vm-1', 'template': '/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'test-vm-1') vm_info = inst.vm_lookup(params['name']) disk_path = '/var/lib/libvirt/images/%s-0.img' % vm_info['uuid'] self.assertTrue(os.access(disk_path, os.F_OK)) self.assertFalse(os.access(disk_path, os.F_OK)) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_storagepool(self): inst = model.Model('qemu:///system', self.tmp_store) poolDefs = [{ 'type': 'dir', 'name': u'kīмсhīUnitTestDirPool', 'path': '/tmp/kimchi-images' }, { 'type': 'iscsi', 'name': u'kīмсhīUnitTestISCSIPool', 'source': { 'host': '127.0.0.1', 'target': 'iqn.2013-12.localhost.kimchiUnitTest' } }] for poolDef in poolDefs: with RollbackContext() as rollback: path = poolDef.get('path') name = poolDef['name'] if poolDef['type'] == 'iscsi': if not TargetClient(**poolDef['source']).validate(): continue pools = inst.storagepools_get_list() num = len(pools) + 1 inst.storagepools_create(poolDef) rollback.prependDefer(inst.storagepool_delete, name) pools = inst.storagepools_get_list() self.assertEquals(num, len(pools)) poolinfo = inst.storagepool_lookup(name) if path is not None: self.assertEquals(path, poolinfo['path']) self.assertEquals('inactive', poolinfo['state']) if poolinfo['type'] == 'dir': self.assertEquals(True, poolinfo['autostart']) else: self.assertEquals(False, poolinfo['autostart']) inst.storagepool_activate(name) rollback.prependDefer(inst.storagepool_deactivate, name) poolinfo = inst.storagepool_lookup(name) self.assertEquals('active', poolinfo['state']) autostart = poolinfo['autostart'] ori_params = { 'autostart': True } if autostart else { 'autostart': False } for i in [True, False]: params = {'autostart': i} inst.storagepool_update(name, params) rollback.prependDefer(inst.storagepool_update, name, ori_params) poolinfo = inst.storagepool_lookup(name) self.assertEquals(i, poolinfo['autostart']) inst.storagepool_update(name, ori_params) pools = inst.storagepools_get_list() self.assertIn('default', pools) poolinfo = inst.storagepool_lookup('default') self.assertEquals('active', poolinfo['state']) self.assertEquals((num - 1), len(pools)) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_storagevolume(self): inst = model.Model('qemu:///system', self.tmp_store) with RollbackContext() as rollback: path = '/tmp/kimchi-images' pool = 'test-pool' vol = 'test-volume.img' if not os.path.exists(path): os.mkdir(path) args = {'name': pool, 'path': path, 'type': 'dir'} inst.storagepools_create(args) rollback.prependDefer(inst.storagepool_delete, pool) self.assertRaises(InvalidOperation, inst.storagevolumes_get_list, pool) poolinfo = inst.storagepool_lookup(pool) self.assertEquals(0, poolinfo['nr_volumes']) # Activate the pool before adding any volume inst.storagepool_activate(pool) rollback.prependDefer(inst.storagepool_deactivate, pool) vols = inst.storagevolumes_get_list(pool) num = len(vols) + 2 params = { 'name': vol, 'capacity': 1024, 'allocation': 512, 'format': 'raw' } inst.storagevolumes_create(pool, params) rollback.prependDefer(inst.storagevolume_delete, pool, vol) fd, path = tempfile.mkstemp(dir=path) name = os.path.basename(path) rollback.prependDefer(inst.storagevolume_delete, pool, name) vols = inst.storagevolumes_get_list(pool) self.assertIn(name, vols) self.assertEquals(num, len(vols)) inst.storagevolume_wipe(pool, vol) volinfo = inst.storagevolume_lookup(pool, vol) self.assertEquals(0, volinfo['allocation']) volinfo = inst.storagevolume_lookup(pool, vol) # Define the size = capacity + 16M capacity = volinfo['capacity'] >> 20 size = capacity + 16 inst.storagevolume_resize(pool, vol, size) volinfo = inst.storagevolume_lookup(pool, vol) self.assertEquals((1024 + 16) << 20, volinfo['capacity']) poolinfo = inst.storagepool_lookup(pool) self.assertEquals(len(vols), poolinfo['nr_volumes']) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_template_storage_customise(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: path = '/tmp/kimchi-images' pool = 'test-pool' if not os.path.exists(path): os.mkdir(path) params = {'name': 'test', 'disks': [{'size': 1}]} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'storagepool': '/storagepools/test-pool'} self.assertRaises(InvalidParameter, inst.template_update, 'test', params) args = {'name': pool, 'path': path, 'type': 'dir'} inst.storagepools_create(args) rollback.prependDefer(inst.storagepool_delete, pool) inst.template_update('test', params) params = {'name': 'test-vm-1', 'template': '/templates/test'} self.assertRaises(InvalidParameter, inst.vms_create, params) inst.storagepool_activate(pool) rollback.prependDefer(inst.storagepool_deactivate, pool) inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'test-vm-1') vm_info = inst.vm_lookup(params['name']) disk_path = '/tmp/kimchi-images/%s-0.img' % vm_info['uuid'] self.assertTrue(os.access(disk_path, os.F_OK)) # reset template to default storage pool # so we can remove the storage pool created 'test-pool' params = {'storagepool': '/storagepools/default'} inst.template_update('test', params) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_template_create(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) # Test non-exist path raises InvalidParameter params = {'name': 'test', 'cdrom': '/non-exsitent.iso'} self.assertRaises(InvalidParameter, inst.templates_create, params) # Test non-iso path raises InvalidParameter params['cdrom'] = os.path.abspath(__file__) self.assertRaises(InvalidParameter, inst.templates_create, params) with RollbackContext() as rollback: net_name = 'test-network' net_args = { 'name': net_name, 'connection': 'nat', 'subnet': '127.0.100.0/24' } inst.networks_create(net_args) rollback.prependDefer(inst.network_delete, net_name) params = {'name': 'test', 'memory': 1024, 'cpus': 1} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') info = inst.template_lookup('test') for key in params.keys(): self.assertEquals(params[key], info[key]) self.assertEquals("default", info["networks"][0]) # create template with non-existent network params['name'] = 'new-test' params['networks'] = ["no-exist"] self.assertRaises(InvalidParameter, inst.templates_create, params) params['networks'] = ['default', 'test-network'] inst.templates_create(params) rollback.prependDefer(inst.template_delete, params['name']) info = inst.template_lookup(params['name']) for key in params.keys(): self.assertEquals(params[key], info[key]) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_template_integrity(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) with RollbackContext() as rollback: net_name = 'test-network' net_args = { 'name': net_name, 'connection': 'nat', 'subnet': '127.0.100.0/24' } inst.networks_create(net_args) path = '/tmp/kimchi-iso/' if not os.path.exists(path): os.makedirs(path) iso = path + 'ubuntu12.04.iso' iso_gen.construct_fake_iso(iso, True, '12.04', 'ubuntu') params = { 'name': 'test', 'memory': 1024, 'cpus': 1, 'networks': ['test-network'], 'cdrom': iso } inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') inst.network_delete(net_name) shutil.rmtree(path) info = inst.template_lookup('test') self.assertEquals(info['invalid']['cdrom'], [iso]) self.assertEquals(info['invalid']['networks'], [net_name]) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_template_clone(self): inst = model.Model('qemu:///system', objstore_loc=self.tmp_store) with RollbackContext() as rollback: orig_params = {'name': 'test-template', 'memory': 1024, 'cpus': 1} inst.templates_create(orig_params) orig_temp = inst.template_lookup(orig_params['name']) ident = inst.template_clone('test-template') clone_temp = inst.template_lookup(ident) clone_temp['name'] = orig_temp['name'] for key in clone_temp.keys(): self.assertEquals(clone_temp[key], orig_temp[key]) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_template_update(self): inst = model.Model('qemu:///system', objstore_loc=self.tmp_store) with RollbackContext() as rollback: net_name = 'test-network' net_args = { 'name': net_name, 'connection': 'nat', 'subnet': '127.0.100.0/24' } inst.networks_create(net_args) rollback.prependDefer(inst.network_delete, net_name) orig_params = {'name': 'test', 'memory': 1024, 'cpus': 1} inst.templates_create(orig_params) params = {'name': 'new-test'} self.assertEquals('new-test', inst.template_update('test', params)) self.assertRaises(NotFoundError, inst.template_delete, 'test') params = {'name': 'new-test', 'memory': 512, 'cpus': 2} inst.template_update('new-test', params) rollback.prependDefer(inst.template_delete, 'new-test') info = inst.template_lookup('new-test') for key in params.keys(): self.assertEquals(params[key], info[key]) self.assertEquals("default", info["networks"][0]) params = { 'name': 'new-test', 'memory': 1024, 'cpus': 1, 'networks': ['default', 'test-network'] } inst.template_update('new-test', params) info = inst.template_lookup('new-test') for key in params.keys(): self.assertEquals(params[key], info[key]) # test update with non-existent network params = {'networks': ["no-exist"]} self.assertRaises(InvalidParameter, inst.template_update, 'new-test', params) def test_vm_edit(self): inst = model.Model('qemu:///system', objstore_loc=self.tmp_store) orig_params = {'name': 'test', 'memory': '1024', 'cpus': '1'} inst.templates_create(orig_params) with RollbackContext() as rollback: params_1 = {'name': 'kimchi-vm1', 'template': '/templates/test'} params_2 = {'name': 'kimchi-vm2', 'template': '/templates/test'} inst.vms_create(params_1) inst.vms_create(params_2) rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, 'kimchi-vm1') rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, 'kimchi-vm2') vms = inst.vms_get_list() self.assertTrue('kimchi-vm1' in vms) inst.vm_start('kimchi-vm1') rollback.prependDefer(self._rollback_wrapper, inst.vm_stop, 'kimchi-vm1') info = inst.vm_lookup('kimchi-vm1') self.assertEquals('running', info['state']) params = {'name': 'new-vm'} self.assertRaises(InvalidParameter, inst.vm_update, 'kimchi-vm1', params) inst.vm_stop('kimchi-vm1') params = {'name': u'пeω-∨м'} self.assertRaises(OperationFailed, inst.vm_update, 'kimchi-vm1', {'name': 'kimchi-vm2'}) inst.vm_update('kimchi-vm1', params) self.assertEquals(info['uuid'], inst.vm_lookup(u'пeω-∨м')['uuid']) rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, u'пeω-∨м') @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_network(self): inst = model.Model('qemu:///system', self.tmp_store) with RollbackContext() as rollback: # Regression test: # Kimchi fails creating new network #318 name = 'test-network-no-subnet' networks = inst.networks_get_list() num = len(networks) + 1 args = {'name': name, 'connection': 'nat', 'subnet': ''} inst.networks_create(args) rollback.prependDefer(inst.network_delete, name) networks = inst.networks_get_list() self.assertEquals(num, len(networks)) networkinfo = inst.network_lookup(name) self.assertNotEqual(args['subnet'], networkinfo['subnet']) self.assertEqual(args['connection'], networkinfo['connection']) self.assertEquals('inactive', networkinfo['state']) self.assertEquals([], networkinfo['vms']) self.assertTrue(networkinfo['autostart']) inst.network_activate(name) rollback.prependDefer(inst.network_deactivate, name) networkinfo = inst.network_lookup(name) self.assertEquals('active', networkinfo['state']) # test network creation with subnet passed name = 'test-network-subnet' networks = inst.networks_get_list() num = len(networks) + 1 args = { 'name': name, 'connection': 'nat', 'subnet': '127.0.100.0/24' } inst.networks_create(args) rollback.prependDefer(inst.network_delete, name) networks = inst.networks_get_list() self.assertEquals(num, len(networks)) networkinfo = inst.network_lookup(name) self.assertEqual(args['subnet'], networkinfo['subnet']) self.assertEqual(args['connection'], networkinfo['connection']) self.assertEquals('inactive', networkinfo['state']) self.assertEquals([], networkinfo['vms']) self.assertTrue(networkinfo['autostart']) inst.network_activate(name) rollback.prependDefer(inst.network_deactivate, name) networkinfo = inst.network_lookup(name) self.assertEquals('active', networkinfo['state']) networks = inst.networks_get_list() self.assertEquals((num - 2), len(networks)) def test_multithreaded_connection(self): def worker(): for i in xrange(100): ret = inst.vms_get_list() self.assertEquals('test', ret[0]) inst = model.Model('test:///default', self.tmp_store) threads = [] for i in xrange(100): t = threading.Thread(target=worker) t.setDaemon(True) t.start() threads.append(t) for t in threads: t.join() def test_object_store(self): store = kimchi.objectstore.ObjectStore(self.tmp_store) with store as session: # Test create session.store('fǒǒ', 'těst1', {'α': 1}) session.store('fǒǒ', 'těst2', {'β': 2}) # Test list items = session.get_list('fǒǒ') self.assertTrue(u'těst1' in items) self.assertTrue(u'těst2' in items) # Test get item = session.get('fǒǒ', 'těst1') self.assertEquals(1, item[u'α']) # Test delete session.delete('fǒǒ', 'těst2') self.assertEquals(1, len(session.get_list('fǒǒ'))) # Test get non-existent item self.assertRaises(NotFoundError, session.get, 'α', 'β') # Test delete non-existent item self.assertRaises(NotFoundError, session.delete, 'fǒǒ', 'těst2') # Test refresh existing item session.store('fǒǒ', 'těst1', {'α': 2}) item = session.get('fǒǒ', 'těst1') self.assertEquals(2, item[u'α']) def test_object_store_threaded(self): def worker(ident): with store as session: session.store('foo', ident, {}) store = kimchi.objectstore.ObjectStore(self.tmp_store) threads = [] for i in xrange(50): t = threading.Thread(target=worker, args=(i, )) t.setDaemon(True) t.start() threads.append(t) for t in threads: t.join() with store as session: self.assertEquals(50, len(session.get_list('foo'))) self.assertEquals(10, len(store._connections.keys())) def test_get_interfaces(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) expected_ifaces = netinfo.all_favored_interfaces() ifaces = inst.interfaces_get_list() self.assertEquals(len(expected_ifaces), len(ifaces)) for name in expected_ifaces: iface = inst.interface_lookup(name) self.assertEquals(iface['name'], name) self.assertIn('type', iface) self.assertIn('status', iface) self.assertIn('ipaddr', iface) self.assertIn('netmask', iface) def test_async_tasks(self): class task_except(Exception): pass def quick_op(cb, message): cb(message, True) def long_op(cb, params): time.sleep(params.get('delay', 3)) cb(params.get('message', ''), params.get('result', False)) def abnormal_op(cb, params): try: raise task_except except: cb("Exception raised", False) inst = model.Model('test:///default', objstore_loc=self.tmp_store) taskid = add_task('', quick_op, inst.objstore, 'Hello') self._wait_task(inst, taskid) self.assertEquals(1, taskid) self.assertEquals('finished', inst.task_lookup(taskid)['status']) self.assertEquals('Hello', inst.task_lookup(taskid)['message']) taskid = add_task('', long_op, inst.objstore, { 'delay': 3, 'result': False, 'message': 'It was not meant to be' }) self.assertEquals(2, taskid) self.assertEquals('running', inst.task_lookup(taskid)['status']) self.assertEquals('OK', inst.task_lookup(taskid)['message']) self._wait_task(inst, taskid) self.assertEquals('failed', inst.task_lookup(taskid)['status']) self.assertEquals('It was not meant to be', inst.task_lookup(taskid)['message']) taskid = add_task('', abnormal_op, inst.objstore, {}) self._wait_task(inst, taskid) self.assertEquals('Exception raised', inst.task_lookup(taskid)['message']) self.assertEquals('failed', inst.task_lookup(taskid)['status']) # This wrapper function is needed due to the new backend messaging in # vm model. vm_stop and vm_delete raise exception if vm is not found. # These functions are called after vm has been deleted if test finishes # correctly, then NofFoundError exception is raised and rollback breaks def _rollback_wrapper(self, func, vmname): try: func(vmname) except NotFoundError: # VM has been deleted already return @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_delete_running_vm(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = {'name': u'test', 'disks': []} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': u'kīмсhī-∨м', 'template': u'/templates/test'} inst.vms_create(params) rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, u'kīмсhī-∨м') inst.vm_start(u'kīмсhī-∨м') rollback.prependDefer(self._rollback_wrapper, inst.vm_stop, u'kīмсhī-∨м') inst.vm_delete(u'kīмсhī-∨м') vms = inst.vms_get_list() self.assertFalse(u'kīмсhī-∨м' in vms) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_list_sorted(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = {'name': 'test', 'disks': []} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': 'kimchi-vm', 'template': '/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'kimchi-vm') vms = inst.vms_get_list() self.assertEquals(vms, sorted(vms, key=unicode.lower)) def test_use_test_host(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = { 'name': 'test', 'disks': [], 'storagepool': '/storagepools/default-pool', 'domain': 'test', 'arch': 'i686' } inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': 'kimchi-vm', 'template': '/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'kimchi-vm') vms = inst.vms_get_list() self.assertTrue('kimchi-vm' in vms) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_debug_reports(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) if not inst.capabilities_lookup()['system_report_tool']: raise unittest.SkipTest("Without debug report tool") try: timeout = int(os.environ['TEST_REPORT_TIMEOUT']) except (ValueError, KeyError): timeout = 120 namePrefix = 'unitTestReport' # sosreport always deletes unsual letters like '-' and '_' in the # generated report file name. uuidstr = str(uuid.uuid4()).translate(None, "-_") reportName = namePrefix + uuidstr try: inst.debugreport_delete(namePrefix + '*') except NotFoundError: pass with RollbackContext() as rollback: report_list = inst.debugreports_get_list() self.assertFalse(reportName in report_list) try: task = inst.debugreports_create({'name': reportName}) rollback.prependDefer(inst.debugreport_delete, reportName) taskid = task['id'] self._wait_task(inst, taskid, timeout) self.assertEquals( 'finished', inst.task_lookup(taskid)['status'], "It is not necessary an error. " "You may need to increase the " "timeout number by " "TEST_REPORT_TIMEOUT=200 " "./run_tests.sh test_model") report_list = inst.debugreports_get_list() self.assertTrue(reportName in report_list) except OperationFailed, e: if not 'debugreport tool not found' in e.message: raise e
def _wait_task(self, model, taskid, timeout=5): for i in range(0, timeout): if model.task_lookup(taskid)['status'] == 'running': time.sleep(1) def test_get_distros(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) distros = inst.distros_get_list() for d in distros: distro = inst.distro_lookup(d) self.assertIn('name', distro) self.assertIn('os_distro', distro) self.assertIn('os_version', distro) self.assertIn('path', distro) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_get_hostinfo(self): inst = model.Model('qemu:///system', objstore_loc=self.tmp_store) info = inst.host_lookup() distro, version, codename = platform.linux_distribution() self.assertIn('cpu', info) self.assertEquals(distro, info['os_distro']) self.assertEquals(version, info['os_version']) self.assertEquals(unicode(codename, "utf-8"), info['os_codename']) self.assertEquals(psutil.TOTAL_PHYMEM, info['memory']) def test_get_hoststats(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) time.sleep(1.5) stats = inst.hoststats_lookup() cpu_utilization = stats['cpu_utilization']
def running_root_and_remoteserver_defined(): return utils.running_as_root() and remoteserver_environment_defined()
class ModelTests(unittest.TestCase): def setUp(self): self.tmp_store = '/tmp/kimchi-store-test' def tearDown(self): os.unlink(self.tmp_store) def test_vm_info(self): inst = kimchi.model.Model('test:///default', self.tmp_store) vms = inst.vms_get_list() self.assertEquals(1, len(vms)) self.assertEquals('test', vms[0]) keys = set(('state', 'stats', 'uuid', 'memory', 'screenshot', 'icon', 'graphics')) stats_keys = set( ('cpu_utilization', 'net_throughput', 'net_throughput_peak', 'io_throughput', 'io_throughput_peak')) info = inst.vm_lookup('test') self.assertEquals(keys, set(info.keys())) self.assertEquals('running', info['state']) self.assertEquals(2048, info['memory']) self.assertEquals(None, info['icon']) self.assertEquals(stats_keys, set(eval(info['stats']).keys())) self.assertRaises(NotFoundError, inst.vm_lookup, 'nosuchvm') @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_lifecycle(self): inst = kimchi.model.Model(objstore_loc=self.tmp_store) with utils.RollbackContext() as rollback: params = {'name': 'test', 'disks': []} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': 'kimchi-vm', 'template': '/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'kimchi-vm') vms = inst.vms_get_list() self.assertTrue('kimchi-vm' in vms) inst.vm_start('kimchi-vm') rollback.prependDefer(inst.vm_stop, 'kimchi-vm') info = inst.vm_lookup('kimchi-vm') self.assertEquals('running', info['state']) vms = inst.vms_get_list() self.assertFalse('kimchi-vm' in vms) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_storage_provisioning(self): inst = kimchi.model.Model(objstore_loc=self.tmp_store) with utils.RollbackContext() as rollback: params = {'name': 'test', 'disks': [{'size': 1}]} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': 'test-vm-1', 'template': '/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'test-vm-1') vm_info = inst.vm_lookup(params['name']) disk_path = '/var/lib/libvirt/images/%s-0.img' % vm_info['uuid'] self.assertTrue(os.access(disk_path, os.F_OK)) self.assertFalse(os.access(disk_path, os.F_OK)) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_storagepool(self): inst = kimchi.model.Model('qemu:///system', self.tmp_store) with utils.RollbackContext() as rollback: path = '/tmp/kimchi-images' name = 'test-pool' if not os.path.exists(path): os.mkdir(path) pools = inst.storagepools_get_list() num = len(pools) + 1 args = {'name': name, 'path': path, 'type': 'dir'} inst.storagepools_create(args) rollback.prependDefer(inst.storagepool_delete, name) pools = inst.storagepools_get_list() self.assertEquals(num, len(pools)) poolinfo = inst.storagepool_lookup(name) self.assertEquals(path, poolinfo['path']) self.assertEquals('inactive', poolinfo['state']) if poolinfo['type'] == 'dir': self.assertEquals(True, poolinfo['autostart']) else: self.assertEquals(False, poolinfo['autostart']) inst.storagepool_activate(name) rollback.prependDefer(inst.storagepool_deactivate, name) poolinfo = inst.storagepool_lookup(name) self.assertEquals('active', poolinfo['state']) autostart = poolinfo['autostart'] ori_params = { 'autostart': True } if autostart else { 'autostart': False } for i in [True, False]: params = {'autostart': i} inst.storagepool_update(name, params) rollback.prependDefer(inst.storagepool_update, name, ori_params) poolinfo = inst.storagepool_lookup(name) self.assertEquals(i, poolinfo['autostart']) inst.storagepool_update(name, ori_params) pools = inst.storagepools_get_list() self.assertIn('default', pools) poolinfo = inst.storagepool_lookup('default') self.assertEquals('active', poolinfo['state']) self.assertEquals((num - 1), len(pools)) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_storagevolume(self): inst = kimchi.model.Model('qemu:///system', self.tmp_store) with utils.RollbackContext() as rollback: path = '/tmp/kimchi-images' pool = 'test-pool' vol = 'test-volume.img' if not os.path.exists(path): os.mkdir(path) args = {'name': pool, 'path': path, 'type': 'dir'} inst.storagepools_create(args) rollback.prependDefer(inst.storagepool_delete, pool) self.assertRaises(InvalidOperation, inst.storagevolumes_get_list, pool) poolinfo = inst.storagepool_lookup(pool) self.assertEquals(0, poolinfo['nr_volumes']) # Activate the pool before adding any volume inst.storagepool_activate(pool) rollback.prependDefer(inst.storagepool_deactivate, pool) vols = inst.storagevolumes_get_list(pool) num = len(vols) + 2 params = { 'name': vol, 'capacity': 1024, 'allocation': 512, 'format': 'raw' } inst.storagevolumes_create(pool, params) rollback.prependDefer(inst.storagevolume_delete, pool, vol) fd, path = tempfile.mkstemp(dir=path) name = os.path.basename(path) rollback.prependDefer(inst.storagevolume_delete, pool, name) vols = inst.storagevolumes_get_list(pool) self.assertIn(name, vols) self.assertEquals(num, len(vols)) inst.storagevolume_wipe(pool, vol) volinfo = inst.storagevolume_lookup(pool, vol) self.assertEquals(0, volinfo['allocation']) volinfo = inst.storagevolume_lookup(pool, vol) # Define the size = capacity + 16M capacity = volinfo['capacity'] >> 20 size = capacity + 16 inst.storagevolume_resize(pool, vol, size) volinfo = inst.storagevolume_lookup(pool, vol) self.assertEquals((1024 + 16) << 20, volinfo['capacity']) poolinfo = inst.storagepool_lookup(pool) self.assertEquals(len(vols), poolinfo['nr_volumes']) def test_template_create(self): inst = kimchi.model.Model('test:///default', objstore_loc=self.tmp_store) # Test non-exist path raises InvalidParameter params = {'name': 'test', 'cdrom': '/non-exsitent.iso'} self.assertRaises(InvalidParameter, inst.templates_create, params) # Test non-iso path raises InvalidParameter params['cdrom'] = os.path.abspath(__file__) self.assertRaises(InvalidParameter, inst.templates_create, params) def test_template_update(self): inst = kimchi.model.Model('test:///default', objstore_loc=self.tmp_store) orig_params = {'name': 'test', 'memory': '1024', 'cpus': '1'} inst.templates_create(orig_params) params = {'name': ' '} self.assertRaises(InvalidParameter, inst.template_update, 'test', params) params = {'memory': ' invalid-value '} self.assertRaises(InvalidParameter, inst.template_update, 'test', params) params = {'memory': ' '} self.assertRaises(InvalidParameter, inst.template_update, 'test', params) params = {'cpus': ' invalid-value '} self.assertRaises(InvalidParameter, inst.template_update, 'test', params) params = {'cpus': ' '} self.assertRaises(InvalidParameter, inst.template_update, 'test', params) params = {'name': 'new-test'} self.assertEquals('new-test', inst.template_update('test', params)) params = {'name': 'new-test', 'memory': '512', 'cpus': '2'} inst.template_update('new-test', params) info = inst.template_lookup('new-test') for key in params.keys(): self.assertEquals(params[key], info[key]) params = {'name': 'new-test', 'memory': 1024, 'cpus': 1} inst.template_update('new-test', params) info = inst.template_lookup('new-test') for key in params.keys(): self.assertEquals(params[key], info[key]) def test_multithreaded_connection(self): def worker(): for i in xrange(100): ret = inst.vms_get_list() self.assertEquals('test', ret[0]) inst = kimchi.model.Model('test:///default', self.tmp_store) threads = [] for i in xrange(100): t = threading.Thread(target=worker) t.setDaemon(True) t.start() threads.append(t) for t in threads: t.join() def test_object_store(self): store = kimchi.objectstore.ObjectStore(self.tmp_store) with store as session: # Test create session.store('fǒǒ', 'těst1', {'α': 1}) session.store('fǒǒ', 'těst2', {'β': 2}) # Test list items = session.get_list('fǒǒ') self.assertTrue(u'těst1' in items) self.assertTrue(u'těst2' in items) # Test get item = session.get('fǒǒ', 'těst1') self.assertEquals(1, item[u'α']) # Test delete session.delete('fǒǒ', 'těst2') self.assertEquals(1, len(session.get_list('fǒǒ'))) # Test get non-existent item self.assertRaises(NotFoundError, session.get, 'α', 'β') # Test delete non-existent item self.assertRaises(NotFoundError, session.delete, 'fǒǒ', 'těst2') # Test refresh existing item session.store('fǒǒ', 'těst1', {'α': 2}) item = session.get('fǒǒ', 'těst1') self.assertEquals(2, item[u'α']) def test_object_store_threaded(self): def worker(ident): with store as session: session.store('foo', ident, {}) store = kimchi.objectstore.ObjectStore(self.tmp_store) threads = [] for i in xrange(50): t = threading.Thread(target=worker, args=(i, )) t.setDaemon(True) t.start() threads.append(t) for t in threads: t.join() with store as session: self.assertEquals(50, len(session.get_list('foo'))) self.assertEquals(10, len(store._connections.keys())) def test_async_tasks(self): class task_except(Exception): pass def wait_task(model, taskid, timeout=5): for i in range(0, timeout): if model.task_lookup(taskid)['status'] == 'running': time.sleep(1) def quick_op(cb, message): cb(message, True) def long_op(cb, params): time.sleep(params.get('delay', 3)) cb(params.get('message', ''), params.get('result', False)) def abnormal_op(cb, params): try: raise task_except except: cb("Exception raised", False) inst = kimchi.model.Model('test:///default', objstore_loc=self.tmp_store) taskid = inst.add_task('', quick_op, 'Hello') wait_task(inst, taskid) self.assertEquals(1, taskid) self.assertEquals('finished', inst.task_lookup(taskid)['status']) self.assertEquals('Hello', inst.task_lookup(taskid)['message']) taskid = inst.add_task('', long_op, { 'delay': 3, 'result': False, 'message': 'It was not meant to be' }) self.assertEquals(2, taskid) self.assertEquals('running', inst.task_lookup(taskid)['status']) self.assertEquals('OK', inst.task_lookup(taskid)['message']) wait_task(inst, taskid) self.assertEquals('failed', inst.task_lookup(taskid)['status']) self.assertEquals('It was not meant to be', inst.task_lookup(taskid)['message']) taskid = inst.add_task('', abnormal_op, {}) wait_task(inst, taskid) self.assertEquals('Exception raised', inst.task_lookup(taskid)['message']) self.assertEquals('failed', inst.task_lookup(taskid)['status']) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_delete_running_vm(self): inst = kimchi.model.Model(objstore_loc=self.tmp_store) with utils.RollbackContext() as rollback: params = {'name': u'test', 'disks': []} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': u'kīмkhī-∨м', 'template': u'/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, u'kīмkhī-∨м') inst.vm_start(u'kīмkhī-∨м') rollback.prependDefer(inst.vm_stop, u'kīмkhī-∨м') inst.vm_delete(u'kīмkhī-∨м') vms = inst.vms_get_list() self.assertFalse(u'kīмkhī-∨м' in vms) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_list_sorted(self): inst = kimchi.model.Model(objstore_loc=self.tmp_store) with utils.RollbackContext() as rollback: params = {'name': 'test', 'disks': []} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': 'kimchi-vm', 'template': '/templates/test'} inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'kimchi-vm') vms = inst.vms_get_list() self.assertEquals(vms, sorted(vms, key=unicode.lower)) def test_use_test_host(self): inst = kimchi.model.Model('test:///default', objstore_loc=self.tmp_store) with utils.RollbackContext() as rollback: params = { 'name': 'test', 'disks': [], 'storagepool': '/storagepools/default-pool', 'domain': 'test', 'arch': 'i686' } inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': 'kimchi-vm', 'template': '/templates/test', } inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'kimchi-vm') vms = inst.vms_get_list() self.assertTrue('kimchi-vm' in vms) def test_get_distros(self): inst = kimchi.model.Model('test:///default', objstore_loc=self.tmp_store) distros = inst._get_distros() for distro in distros.values(): self.assertIn('name', distro) self.assertIn('os_distro', distro) self.assertIn('os_version', distro) self.assertIn('path', distro)
class ModelTests(unittest.TestCase): def setUp(self): self.tmp_store = '/tmp/kimchi-store-test' def tearDown(self): # FIXME: Tests using 'test:///default' URI should be moved to # test_rest or test_mockmodel to avoid overriding problems LibvirtConnection._connections['test:///default'] = {} os.unlink(self.tmp_store) def test_vm_info(self): inst = model.Model('test:///default', self.tmp_store) vms = inst.vms_get_list() self.assertEquals(1, len(vms)) self.assertEquals('test', vms[0]) keys = set( ('name', 'state', 'stats', 'uuid', 'memory', 'cpus', 'screenshot', 'icon', 'graphics', 'users', 'groups', 'access', 'persistent')) stats_keys = set( ('cpu_utilization', 'mem_utilization', 'net_throughput', 'net_throughput_peak', 'io_throughput', 'io_throughput_peak')) info = inst.vm_lookup('test') self.assertEquals(keys, set(info.keys())) self.assertEquals('running', info['state']) self.assertEquals('test', info['name']) self.assertEquals(2048, info['memory']) self.assertEquals(2, info['cpus']) self.assertEquals(None, info['icon']) self.assertEquals(stats_keys, set(info['stats'].keys())) self.assertRaises(NotFoundError, inst.vm_lookup, 'nosuchvm') self.assertEquals([], info['users']) self.assertEquals([], info['groups']) self.assertTrue(info['persistent']) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_lifecycle(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: vol_params = {'name': u'test-vol', 'capacity': 1024} task = inst.storagevolumes_create(u'default', vol_params) rollback.prependDefer(inst.storagevolume_delete, u'default', vol_params['name']) inst.task_wait(task['id']) task = inst.task_lookup(task['id']) self.assertEquals('finished', task['status']) vol = inst.storagevolume_lookup(u'default', vol_params['name']) params = { 'name': 'test', 'disks': [{ 'base': vol['path'], 'size': 1 }], 'cdrom': UBUNTU_ISO } inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': 'kimchi-vm', 'template': '/plugins/kimchi/templates/test' } task = inst.vms_create(params) rollback.prependDefer(inst.vm_delete, 'kimchi-vm') inst.task_wait(task['id'], 10) task = inst.task_lookup(task['id']) self.assertEquals('finished', task['status']) vms = inst.vms_get_list() self.assertTrue('kimchi-vm' in vms) inst.vm_start('kimchi-vm') info = inst.vm_lookup('kimchi-vm') self.assertEquals('running', info['state']) self.assertRaises(InvalidOperation, inst.vmsnapshots_create, u'kimchi-vm') inst.vm_poweroff(u'kimchi-vm') vm = inst.vm_lookup(u'kimchi-vm') empty_snap = inst.currentvmsnapshot_lookup(u'kimchi-vm') self.assertEquals({}, empty_snap) # this snapshot should be deleted when its VM is deleted params = {'name': u'mysnap'} task = inst.vmsnapshots_create(u'kimchi-vm', params) inst.task_wait(task['id']) task = inst.task_lookup(task['id']) self.assertEquals('finished', task['status']) self.assertRaises(NotFoundError, inst.vmsnapshot_lookup, u'kimchi-vm', u'foobar') snap = inst.vmsnapshot_lookup(u'kimchi-vm', params['name']) self.assertTrue(int(time.time()) >= int(snap['created'])) self.assertEquals(vm['state'], snap['state']) self.assertEquals(params['name'], snap['name']) self.assertEquals(u'', snap['parent']) snaps = inst.vmsnapshots_get_list(u'kimchi-vm') self.assertEquals([params['name']], snaps) current_snap = inst.currentvmsnapshot_lookup(u'kimchi-vm') self.assertEquals(snap, current_snap) task = inst.vmsnapshots_create(u'kimchi-vm') snap_name = task['target_uri'].split('/')[-1] rollback.prependDefer(inst.vmsnapshot_delete, u'kimchi-vm', snap_name) inst.task_wait(task['id']) task = inst.task_lookup(task['id']) self.assertEquals('finished', task['status']) snaps = inst.vmsnapshots_get_list(u'kimchi-vm') self.assertEquals( sorted([params['name'], snap_name], key=unicode.lower), snaps) snap = inst.vmsnapshot_lookup(u'kimchi-vm', snap_name) current_snap = inst.currentvmsnapshot_lookup(u'kimchi-vm') self.assertEquals(snap, current_snap) # update vm name inst.vm_update('kimchi-vm', {'name': u'kimchi-vm-new'}) # Look up the first created snapshot from the renamed vm snap = inst.vmsnapshot_lookup(u'kimchi-vm-new', params['name']) # snapshot revert to the first created vm result = inst.vmsnapshot_revert(u'kimchi-vm-new', params['name']) self.assertEquals(result, [u'kimchi-vm', snap['name']]) vm = inst.vm_lookup(u'kimchi-vm') self.assertEquals(vm['state'], snap['state']) current_snap = inst.currentvmsnapshot_lookup(u'kimchi-vm') self.assertEquals(params['name'], current_snap['name']) self.assertRaises(NotFoundError, inst.vmsnapshot_delete, u'kimchi-vm', u'foobar') # suspend and resume the VM info = inst.vm_lookup(u'kimchi-vm') self.assertEquals(info['state'], 'shutoff') self.assertRaises(InvalidOperation, inst.vm_suspend, u'kimchi-vm') inst.vm_start(u'kimchi-vm') info = inst.vm_lookup(u'kimchi-vm') self.assertEquals(info['state'], 'running') inst.vm_suspend(u'kimchi-vm') info = inst.vm_lookup(u'kimchi-vm') self.assertEquals(info['state'], 'paused') self.assertRaises(InvalidParameter, inst.vm_update, u'kimchi-vm', {'name': 'foo'}) inst.vm_resume(u'kimchi-vm') info = inst.vm_lookup(u'kimchi-vm') self.assertEquals(info['state'], 'running') self.assertRaises(InvalidOperation, inst.vm_resume, u'kimchi-vm') # leave the VM suspended to make sure a paused VM can be # deleted correctly inst.vm_suspend(u'kimchi-vm') vms = inst.vms_get_list() self.assertFalse('kimchi-vm' in vms) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_image_based_template(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: vol = 'base-vol.img' params = { 'name': vol, 'capacity': 1073741824, # 1 GiB 'allocation': 1048576, # 1 MiB 'format': 'qcow2' } task_id = inst.storagevolumes_create('default', params)['id'] rollback.prependDefer(inst.storagevolume_delete, 'default', vol) inst.task_wait(task_id) self.assertEquals('finished', inst.task_lookup(task_id)['status']) vol_path = inst.storagevolume_lookup('default', vol)['path'] # Hack the model objstore to add a new template # It is needed as the image file must be a bootable image when # using model # As it is difficult to create one on test runtime, inject a # template with an empty image file to the objstore to test the # feature tmpl_name = "img-tmpl" tmpl_info = { "cpus": 1, "cdrom": "", "graphics": { "type": "vnc", "listen": "127.0.0.1" }, "networks": ["default"], "memory": 1024, "folder": [], "icon": "images/icon-vm.png", "os_distro": "unknown", "os_version": "unknown", "disks": [{ "base": vol_path, "size": 10 }], "storagepool": "/plugins/kimchi/storagepools/default" } with inst.objstore as session: session.store('template', tmpl_name, tmpl_info) params = { 'name': 'kimchi-vm', 'template': '/plugins/kimchi/templates/img-tmpl' } task = inst.vms_create(params) inst.task_wait(task['id']) rollback.prependDefer(inst.vm_delete, 'kimchi-vm') vms = inst.vms_get_list() self.assertTrue('kimchi-vm' in vms) inst.vm_start('kimchi-vm') rollback.prependDefer(inst.vm_poweroff, 'kimchi-vm') info = inst.vm_lookup('kimchi-vm') self.assertEquals('running', info['state']) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_graphics(self): inst = model.Model(objstore_loc=self.tmp_store) params = {'name': 'test', 'disks': [], 'cdrom': UBUNTU_ISO} inst.templates_create(params) with RollbackContext() as rollback: params = { 'name': 'kimchi-vnc', 'template': '/plugins/kimchi/templates/test' } task1 = inst.vms_create(params) inst.task_wait(task1['id']) rollback.prependDefer(inst.vm_delete, 'kimchi-vnc') info = inst.vm_lookup('kimchi-vnc') self.assertEquals('vnc', info['graphics']['type']) self.assertEquals('127.0.0.1', info['graphics']['listen']) graphics = {'type': 'spice', 'listen': '127.0.0.1'} params = { 'name': 'kimchi-spice', 'template': '/plugins/kimchi/templates/test', 'graphics': graphics } task2 = inst.vms_create(params) inst.task_wait(task2['id']) rollback.prependDefer(inst.vm_delete, 'kimchi-spice') info = inst.vm_lookup('kimchi-spice') self.assertEquals('spice', info['graphics']['type']) self.assertEquals('127.0.0.1', info['graphics']['listen']) inst.template_delete('test') @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_ifaces(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = {'name': 'test', 'disks': [], 'cdrom': UBUNTU_ISO} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') # Create a network net_name = 'test-network' net_args = { 'name': net_name, 'connection': 'nat', 'subnet': '127.0.100.0/24' } inst.networks_create(net_args) rollback.prependDefer(inst.network_delete, net_name) inst.network_activate(net_name) rollback.prependDefer(inst.network_deactivate, net_name) for vm_name in ['kimchi-ifaces', 'kimchi-ifaces-running']: params = { 'name': vm_name, 'template': '/plugins/kimchi/templates/test' } task = inst.vms_create(params) inst.task_wait(task['id']) rollback.prependDefer(inst.vm_delete, vm_name) ifaces = inst.vmifaces_get_list(vm_name) self.assertEquals(1, len(ifaces)) iface = inst.vmiface_lookup(vm_name, ifaces[0]) self.assertEquals(17, len(iface['mac'])) self.assertEquals("default", iface['network']) self.assertIn("model", iface) # attach network interface to vm iface_args = { "type": "network", "network": "test-network", "model": "virtio" } mac = inst.vmifaces_create(vm_name, iface_args) # detach network interface from vm rollback.prependDefer(inst.vmiface_delete, vm_name, mac) self.assertEquals(17, len(mac)) iface = inst.vmiface_lookup(vm_name, mac) self.assertEquals("network", iface["type"]) self.assertEquals("test-network", iface['network']) self.assertEquals("virtio", iface["model"]) # attach network interface to vm without providing model iface_args = {"type": "network", "network": "test-network"} mac = inst.vmifaces_create(vm_name, iface_args) rollback.prependDefer(inst.vmiface_delete, vm_name, mac) iface = inst.vmiface_lookup(vm_name, mac) self.assertEquals("network", iface["type"]) self.assertEquals("test-network", iface['network']) # update vm interface newMacAddr = '54:50:e3:44:8a:af' iface_args = {"mac": newMacAddr} inst.vmiface_update(vm_name, mac, iface_args) iface = inst.vmiface_lookup(vm_name, newMacAddr) self.assertEquals(newMacAddr, iface['mac']) # undo mac address change iface_args = {"mac": mac} inst.vmiface_update(vm_name, newMacAddr, iface_args) iface = inst.vmiface_lookup(vm_name, mac) self.assertEquals(mac, iface['mac']) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_disk(self): disk_path = os.path.join(TMP_DIR, 'existent2.iso') open(disk_path, 'w').close() modern_disk_bus = osinfo.get_template_default('modern', 'disk_bus') def _attach_disk(expect_bus=modern_disk_bus): disk_args = {"type": "disk", "pool": pool, "vol": vol} disk = inst.vmstorages_create(vm_name, disk_args) storage_list = inst.vmstorages_get_list(vm_name) self.assertEquals(prev_count + 1, len(storage_list)) # Check the bus type to be 'virtio' disk_info = inst.vmstorage_lookup(vm_name, disk) self.assertEquals(u'disk', disk_info['type']) self.assertEquals(vol_path, disk_info['path']) self.assertEquals(expect_bus, disk_info['bus']) return disk inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: path = os.path.join(TMP_DIR, 'kimchi-images') pool = 'test-pool' vol = 'test-volume.img' vol_path = "%s/%s" % (path, vol) if not os.path.exists(path): os.mkdir(path) rollback.prependDefer(shutil.rmtree, path) args = {'name': pool, 'path': path, 'type': 'dir'} inst.storagepools_create(args) rollback.prependDefer(inst.storagepool_delete, pool) # Activate the pool before adding any volume inst.storagepool_activate(pool) rollback.prependDefer(inst.storagepool_deactivate, pool) params = { 'name': vol, 'capacity': 1073741824, # 1 GiB 'allocation': 536870912, # 512 MiB 'format': 'qcow2' } task_id = inst.storagevolumes_create(pool, params)['id'] rollback.prependDefer(inst.storagevolume_delete, pool, vol) inst.task_wait(task_id) vm_name = 'kimchi-cdrom' params = {'name': 'test', 'disks': [], 'cdrom': UBUNTU_ISO} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': vm_name, 'template': '/plugins/kimchi/templates/test' } task1 = inst.vms_create(params) inst.task_wait(task1['id']) rollback.prependDefer(inst.vm_delete, vm_name) prev_count = len(inst.vmstorages_get_list(vm_name)) self.assertEquals(1, prev_count) # Volume format with mismatched type raise error cdrom_args = {"type": "cdrom", "pool": pool, "vol": vol} self.assertRaises(InvalidParameter, inst.vmstorages_create, vm_name, cdrom_args) # Cold plug and unplug a disk disk = _attach_disk() inst.vmstorage_delete(vm_name, disk) # Hot plug a disk inst.vm_start(vm_name) disk = _attach_disk() # VM disk still there after powered off inst.vm_poweroff(vm_name) disk_info = inst.vmstorage_lookup(vm_name, disk) self.assertEquals(u'disk', disk_info['type']) inst.vmstorage_delete(vm_name, disk) # Specifying pool and path at same time will fail disk_args = { "type": "disk", "pool": pool, "vol": vol, "path": disk_path } self.assertRaises(InvalidParameter, inst.vmstorages_create, vm_name, disk_args) old_distro_iso = TMP_DIR + 'rhel4_8.iso' iso_gen.construct_fake_iso(old_distro_iso, True, '4.8', 'rhel') vm_name = 'kimchi-ide-bus-vm' params = { 'name': 'old_distro_template', 'disks': [], 'cdrom': old_distro_iso } inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'old_distro_template') params = { 'name': vm_name, 'template': '/plugins/kimchi/templates/old_distro_template' } task2 = inst.vms_create(params) inst.task_wait(task2['id']) rollback.prependDefer(inst.vm_delete, vm_name) # Need to check the right disk_bus for old distro disk = _attach_disk(osinfo.get_template_default('old', 'disk_bus')) inst.vmstorage_delete('kimchi-ide-bus-vm', disk) # Hot plug IDE bus disk does not work inst.vm_start(vm_name) self.assertRaises(InvalidOperation, _attach_disk) inst.vm_poweroff(vm_name) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_cdrom(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: vm_name = 'kimchi-cdrom' params = {'name': 'test', 'disks': [], 'cdrom': UBUNTU_ISO} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': vm_name, 'template': '/plugins/kimchi/templates/test' } task = inst.vms_create(params) inst.task_wait(task['id']) rollback.prependDefer(inst.vm_delete, vm_name) prev_count = len(inst.vmstorages_get_list(vm_name)) self.assertEquals(1, prev_count) # dummy .iso files iso_path = os.path.join(TMP_DIR, 'existent.iso') iso_path2 = os.path.join(TMP_DIR, 'existent2.iso') open(iso_path, 'w').close() rollback.prependDefer(os.remove, iso_path) open(iso_path2, 'w').close() rollback.prependDefer(os.remove, iso_path2) wrong_iso_path = '/nonexistent.iso' # Create a cdrom cdrom_args = {"type": "cdrom", "path": iso_path} cdrom_dev = inst.vmstorages_create(vm_name, cdrom_args) storage_list = inst.vmstorages_get_list(vm_name) self.assertEquals(prev_count + 1, len(storage_list)) # Get cdrom info cd_info = inst.vmstorage_lookup(vm_name, cdrom_dev) self.assertEquals(u'cdrom', cd_info['type']) self.assertEquals(iso_path, cd_info['path']) # update path of existing cd with # non existent iso self.assertRaises(InvalidParameter, inst.vmstorage_update, vm_name, cdrom_dev, {'path': wrong_iso_path}) # Make sure CD ROM still exists after failure cd_info = inst.vmstorage_lookup(vm_name, cdrom_dev) self.assertEquals(u'cdrom', cd_info['type']) self.assertEquals(iso_path, cd_info['path']) # update path of existing cd with existent iso of shutoff vm inst.vmstorage_update(vm_name, cdrom_dev, {'path': iso_path2}) cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev) self.assertEquals(iso_path2, cdrom_info['path']) # update path of existing cd with existent iso of running vm inst.vm_start(vm_name) inst.vmstorage_update(vm_name, cdrom_dev, {'path': iso_path}) cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev) self.assertEquals(iso_path, cdrom_info['path']) # eject cdrom cdrom_dev = inst.vmstorage_update(vm_name, cdrom_dev, {'path': ''}) cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev) self.assertEquals('', cdrom_info['path']) inst.vm_poweroff(vm_name) # removing non existent cdrom self.assertRaises(NotFoundError, inst.vmstorage_delete, vm_name, "fakedev") # removing valid cdrom inst.vmstorage_delete(vm_name, cdrom_dev) storage_list = inst.vmstorages_get_list(vm_name) self.assertEquals(prev_count, len(storage_list)) # Create a new cdrom using a remote iso valid_remote_iso_path = utils.get_remote_iso_path() cdrom_args = {"type": "cdrom", "path": valid_remote_iso_path} cdrom_dev = inst.vmstorages_create(vm_name, cdrom_args) storage_list = inst.vmstorages_get_list(vm_name) self.assertEquals(prev_count + 1, len(storage_list)) # Update remote-backed cdrom with the same ISO inst.vmstorage_update(vm_name, cdrom_dev, {'path': valid_remote_iso_path}) cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev) cur_cdrom_path = re.sub(":80/", '/', cdrom_info['path']) self.assertEquals(valid_remote_iso_path, cur_cdrom_path) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_storage_provisioning(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = { 'name': 'test', 'disks': [{ 'size': 1 }], 'cdrom': UBUNTU_ISO } inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': 'test-vm-1', 'template': '/plugins/kimchi/templates/test' } task = inst.vms_create(params) inst.task_wait(task['id']) rollback.prependDefer(inst.vm_delete, 'test-vm-1') vm_info = inst.vm_lookup(params['name']) disk_path = '%s/%s-0.img' % ( inst.storagepool_lookup('default')['path'], vm_info['uuid']) self.assertTrue(os.access(disk_path, os.F_OK)) self.assertFalse(os.access(disk_path, os.F_OK)) def _create_template_conf_with_disk_format(self, vol_format): if vol_format is None: conf_file_data = "[main]\n\n[storage]\n\n[[disk.0]]\n" \ "#format = \n\n[graphics]\n\n[processor]\n" else: conf_file_data = "[main]\n\n[storage]\n\n[[disk.0]]\n" \ "format = %s\n\n[graphics]\n\n[processor]\n"\ % vol_format config_file = os.path.join(paths.conf_dir, 'template.conf') config_bkp_file = \ os.path.join(paths.conf_dir, 'template.conf-unit_test_bkp') os.rename(config_file, config_bkp_file) with open(config_file, 'w') as f: f.write(conf_file_data) osinfo.defaults = osinfo._get_tmpl_defaults() def _restore_template_conf_file(self): config_file = os.path.join(paths.conf_dir, 'template.conf') config_bkp_file = \ os.path.join(paths.conf_dir, 'template.conf-unit_test_bkp') os.rename(config_bkp_file, config_file) osinfo.defaults = osinfo._get_tmpl_defaults() def _get_disk_format_from_vm(self, vm, conn): dom = VMModel.get_vm(vm, conn) xml = dom.XMLDesc(0) xpath = "/domain/devices/disk[@device='disk']/driver/@type" return xpath_get_text(xml, xpath)[0] @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_template_get_default_vol_format_from_conf(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: self._create_template_conf_with_disk_format('vmdk') rollback.prependDefer(self._restore_template_conf_file) params = { 'name': 'test', 'disks': [{ 'size': 1 }], 'cdrom': UBUNTU_ISO } inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': 'test-vm-1', 'template': '/plugins/kimchi/templates/test' } task = inst.vms_create(params) inst.task_wait(task['id']) rollback.prependDefer(inst.vm_delete, 'test-vm-1') created_disk_format = self._get_disk_format_from_vm( 'test-vm-1', inst.conn) self.assertEqual(created_disk_format, 'vmdk') @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_template_creates_user_defined_vol_format_instead_default(self): inst = model.Model(objstore_loc=self.tmp_store) default_vol = 'vmdk' user_vol = 'raw' with RollbackContext() as rollback: self._create_template_conf_with_disk_format(default_vol) rollback.prependDefer(self._restore_template_conf_file) params = { 'name': 'test', 'disks': [{ 'size': 1, 'format': user_vol }], 'cdrom': UBUNTU_ISO } inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': 'test-vm-1', 'template': '/plugins/kimchi/templates/test' } task = inst.vms_create(params) inst.task_wait(task['id']) rollback.prependDefer(inst.vm_delete, 'test-vm-1') created_disk_format = self._get_disk_format_from_vm( 'test-vm-1', inst.conn) self.assertEqual(created_disk_format, user_vol) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_template_uses_qcow2_format_if_no_user_or_default_defined(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: self._create_template_conf_with_disk_format(None) rollback.prependDefer(self._restore_template_conf_file) params = { 'name': 'test', 'disks': [{ 'size': 1 }], 'cdrom': UBUNTU_ISO } inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': 'test-vm-1', 'template': '/plugins/kimchi/templates/test' } task = inst.vms_create(params) inst.task_wait(task['id']) rollback.prependDefer(inst.vm_delete, 'test-vm-1') created_disk_format = self._get_disk_format_from_vm( 'test-vm-1', inst.conn) self.assertEqual(created_disk_format, 'qcow2') def test_vm_memory_hotplug(self): config.set("authentication", "method", "pam") inst = model.Model(None, objstore_loc=self.tmp_store) orig_params = {'name': 'test', 'memory': 1024, 'cdrom': UBUNTU_ISO} inst.templates_create(orig_params) with RollbackContext() as rollback: params = { 'name': 'kimchi-vm1', 'template': '/plugins/kimchi/templates/test' } task1 = inst.vms_create(params) inst.task_wait(task1['id']) rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, 'kimchi-vm1') # Start vm inst.vm_start('kimchi-vm1') rollback.prependDefer(utils.rollback_wrapper, inst.vm_poweroff, 'kimchi-vm1') # Hotplug memory, only available in Libvirt >= 1.2.14 params = {'memory': 2048} if inst.capabilities_lookup()['mem_hotplug_support']: inst.vm_update('kimchi-vm1', params) rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, 'kimchi-vm1') self.assertEquals(params['memory'], inst.vm_lookup('kimchi-vm1')['memory']) else: self.assertRaises(InvalidOperation, inst.vm_update, 'kimchi-vm1', params) def test_vm_edit(self): config.set("authentication", "method", "pam") inst = model.Model(None, objstore_loc=self.tmp_store) # template disk format must be qcow2 because vmsnapshot # only supports this format orig_params = { 'name': 'test', 'memory': 1024, 'cpus': 1, 'cdrom': UBUNTU_ISO, 'disks': [{ 'size': 1, 'format': 'qcow2' }] } inst.templates_create(orig_params) with RollbackContext() as rollback: params_1 = { 'name': 'kimchi-vm1', 'template': '/plugins/kimchi/templates/test' } params_2 = { 'name': 'kimchi-vm2', 'template': '/plugins/kimchi/templates/test' } task1 = inst.vms_create(params_1) inst.task_wait(task1['id']) rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, 'kimchi-vm1') task2 = inst.vms_create(params_2) inst.task_wait(task2['id']) rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, 'kimchi-vm2') vms = inst.vms_get_list() self.assertTrue('kimchi-vm1' in vms) # make sure "vm_update" works when the domain has a snapshot inst.vmsnapshots_create(u'kimchi-vm1') # update vm graphics when vm is not running inst.vm_update(u'kimchi-vm1', {"graphics": {"passwd": "123456"}}) inst.vm_start('kimchi-vm1') rollback.prependDefer(utils.rollback_wrapper, inst.vm_poweroff, 'kimchi-vm1') vm_info = inst.vm_lookup(u'kimchi-vm1') self.assertEquals('123456', vm_info['graphics']["passwd"]) self.assertEquals(None, vm_info['graphics']["passwdValidTo"]) # update vm graphics when vm is running inst.vm_update( u'kimchi-vm1', {"graphics": { "passwd": "abcdef", "passwdValidTo": 20 }}) vm_info = inst.vm_lookup(u'kimchi-vm1') self.assertEquals('abcdef', vm_info['graphics']["passwd"]) self.assertGreaterEqual(20, vm_info['graphics']['passwdValidTo']) info = inst.vm_lookup('kimchi-vm1') self.assertEquals('running', info['state']) params = {'name': 'new-vm'} self.assertRaises(InvalidParameter, inst.vm_update, 'kimchi-vm1', params) # change VM users and groups, when wm is running. inst.vm_update(u'kimchi-vm1', { 'users': ['root'], 'groups': ['root'] }) vm_info = inst.vm_lookup(u'kimchi-vm1') self.assertEquals(['root'], vm_info['users']) self.assertEquals(['root'], vm_info['groups']) # change VM users and groups by removing all elements, # when wm is running. inst.vm_update(u'kimchi-vm1', {'users': [], 'groups': []}) vm_info = inst.vm_lookup(u'kimchi-vm1') self.assertEquals([], vm_info['users']) self.assertEquals([], vm_info['groups']) inst.vm_poweroff('kimchi-vm1') self.assertRaises(OperationFailed, inst.vm_update, 'kimchi-vm1', {'name': 'kimchi-vm2'}) params = {'name': u'пeω-∨м', 'cpus': 4, 'memory': 2048} inst.vm_update('kimchi-vm1', params) rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, u'пeω-∨м') self.assertEquals(info['uuid'], inst.vm_lookup(u'пeω-∨м')['uuid']) info = inst.vm_lookup(u'пeω-∨м') for key in params.keys(): self.assertEquals(params[key], info[key]) # change only VM users - groups are not changed (default is empty) users = inst.users_get_list()[:3] inst.vm_update(u'пeω-∨м', {'users': users}) self.assertEquals(users, inst.vm_lookup(u'пeω-∨м')['users']) self.assertEquals([], inst.vm_lookup(u'пeω-∨м')['groups']) # change only VM groups - users are not changed (default is empty) groups = inst.groups_get_list()[:2] inst.vm_update(u'пeω-∨м', {'groups': groups}) self.assertEquals(users, inst.vm_lookup(u'пeω-∨м')['users']) self.assertEquals(groups, inst.vm_lookup(u'пeω-∨м')['groups']) # change VM users and groups by adding a new element to each one users.append(pwd.getpwuid(os.getuid()).pw_name) groups.append(grp.getgrgid(os.getgid()).gr_name) inst.vm_update(u'пeω-∨м', {'users': users, 'groups': groups}) self.assertEquals(users, inst.vm_lookup(u'пeω-∨м')['users']) self.assertEquals(groups, inst.vm_lookup(u'пeω-∨м')['groups']) # change VM users (wrong value) and groups # when an error occurs, everything fails and nothing is changed self.assertRaises(InvalidParameter, inst.vm_update, u'пeω-∨м', { 'users': ['userdoesnotexist'], 'groups': [] }) self.assertEquals(users, inst.vm_lookup(u'пeω-∨м')['users']) self.assertEquals(groups, inst.vm_lookup(u'пeω-∨м')['groups']) # change VM users and groups (wrong value) # when an error occurs, everything fails and nothing is changed self.assertRaises(InvalidParameter, inst.vm_update, u'пeω-∨м', { 'users': [], 'groups': ['groupdoesnotexist'] }) self.assertEquals(users, inst.vm_lookup(u'пeω-∨м')['users']) self.assertEquals(groups, inst.vm_lookup(u'пeω-∨м')['groups']) # change VM users and groups by removing all elements inst.vm_update(u'пeω-∨м', {'users': [], 'groups': []}) self.assertEquals([], inst.vm_lookup(u'пeω-∨м')['users']) self.assertEquals([], inst.vm_lookup(u'пeω-∨м')['groups']) def test_get_interfaces(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) expected_ifaces = netinfo.all_favored_interfaces() ifaces = inst.interfaces_get_list() self.assertEquals(len(expected_ifaces), len(ifaces)) for name in expected_ifaces: iface = inst.interface_lookup(name) self.assertEquals(iface['name'], name) self.assertIn('type', iface) self.assertIn('status', iface) self.assertIn('ipaddr', iface) self.assertIn('netmask', iface) def test_async_tasks(self): class task_except(Exception): pass def quick_op(cb, message): cb(message, True) def long_op(cb, params): time.sleep(params.get('delay', 3)) cb(params.get('message', ''), params.get('result', False)) def abnormal_op(cb, params): try: raise task_except except: cb("Exception raised", False) def continuous_ops(cb, params): cb("step 1 OK") time.sleep(2) cb("step 2 OK") time.sleep(2) cb("step 3 OK", params.get('result', True)) inst = model.Model('test:///default', objstore_loc=self.tmp_store) taskid = add_task('', quick_op, inst.objstore, 'Hello') inst.task_wait(taskid) self.assertEquals(1, taskid) self.assertEquals('finished', inst.task_lookup(taskid)['status']) self.assertEquals('Hello', inst.task_lookup(taskid)['message']) taskid = add_task('', long_op, inst.objstore, { 'delay': 3, 'result': False, 'message': 'It was not meant to be' }) self.assertEquals(2, taskid) self.assertEquals('running', inst.task_lookup(taskid)['status']) self.assertEquals('OK', inst.task_lookup(taskid)['message']) inst.task_wait(taskid) self.assertEquals('failed', inst.task_lookup(taskid)['status']) self.assertEquals('It was not meant to be', inst.task_lookup(taskid)['message']) taskid = add_task('', abnormal_op, inst.objstore, {}) inst.task_wait(taskid) self.assertEquals('Exception raised', inst.task_lookup(taskid)['message']) self.assertEquals('failed', inst.task_lookup(taskid)['status']) taskid = add_task('', continuous_ops, inst.objstore, {'result': True}) self.assertEquals('running', inst.task_lookup(taskid)['status']) inst.task_wait(taskid, timeout=10) self.assertEquals('finished', inst.task_lookup(taskid)['status']) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_delete_running_vm(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = {'name': u'test', 'disks': [], 'cdrom': UBUNTU_ISO} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': u'kīмсhī-∨м', 'template': u'/plugins/kimchi/templates/test' } task = inst.vms_create(params) inst.task_wait(task['id']) rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, u'kīмсhī-∨м') inst.vm_start(u'kīмсhī-∨м') self.assertEquals(inst.vm_lookup(u'kīмсhī-∨м')['state'], 'running') rollback.prependDefer(utils.rollback_wrapper, inst.vm_poweroff, u'kīмсhī-∨м') inst.vm_delete(u'kīмсhī-∨м') vms = inst.vms_get_list() self.assertFalse(u'kīмсhī-∨м' in vms) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_list_sorted(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = {'name': 'test', 'disks': [], 'cdrom': UBUNTU_ISO} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': 'kimchi-vm', 'template': '/plugins/kimchi/templates/test' } task = inst.vms_create(params) inst.task_wait(task['id']) rollback.prependDefer(inst.vm_delete, 'kimchi-vm') vms = inst.vms_get_list() self.assertEquals(vms, sorted(vms, key=unicode.lower)) def test_vm_clone(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) all_vm_names = inst.vms_get_list() name = all_vm_names[0] original_vm = inst.vm_lookup(name) if original_vm['state'] == u'shutoff': inst.vm_start(name) # the VM 'test' should be running by now, so we can't clone it yet self.assertRaises(InvalidParameter, inst.vm_clone, name) with RollbackContext() as rollback: inst.vm_poweroff(name) rollback.prependDefer(inst.vm_start, name) # create two simultaneous clones of the same VM # and make sure both of them complete successfully task1 = inst.vm_clone(name) task2 = inst.vm_clone(name) clone1_name = task1['target_uri'].split('/')[-2] rollback.prependDefer(inst.vm_delete, clone1_name) clone2_name = task2['target_uri'].split('/')[-2] rollback.prependDefer(inst.vm_delete, clone2_name) inst.task_wait(task1['id']) task1 = inst.task_lookup(task1['id']) self.assertEquals('finished', task1['status']) inst.task_wait(task2['id']) task2 = inst.task_lookup(task2['id']) self.assertEquals('finished', task2['status']) # update the original VM info because its state has changed original_vm = inst.vm_lookup(name) clone_vm = inst.vm_lookup(clone1_name) self.assertNotEqual(original_vm['name'], clone_vm['name']) self.assertTrue( re.match(u'%s-clone-\d+' % original_vm['name'], clone_vm['name'])) del original_vm['name'] del clone_vm['name'] self.assertNotEqual(original_vm['uuid'], clone_vm['uuid']) del original_vm['uuid'] del clone_vm['uuid'] # compare all VM settings except the ones already compared # (and removed) above (i.e. 'name' and 'uuid') self.assertEquals(original_vm, clone_vm) def test_use_test_host(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) with RollbackContext() as rollback: params = { 'name': 'test', 'disks': [], 'cdrom': UBUNTU_ISO, 'storagepool': '/plugins/kimchi/storagepools/default-pool', 'domain': 'test', 'arch': 'i686' } inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = { 'name': 'kimchi-vm', 'template': '/plugins/kimchi/templates/test' } task = inst.vms_create(params) inst.task_wait(task['id']) rollback.prependDefer(inst.vm_delete, 'kimchi-vm') vms = inst.vms_get_list() self.assertTrue('kimchi-vm' in vms) def test_get_distros(self): inst = model.Model('test:///default', objstore_loc=self.tmp_store) distros = inst.distros_get_list() for d in distros: distro = inst.distro_lookup(d) self.assertIn('name', distro) self.assertIn('os_distro', distro) self.assertIn('os_version', distro) self.assertIn('os_arch', distro) self.assertIn('path', distro) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_deep_scan(self): inst = model.Model(None, objstore_loc=self.tmp_store) with RollbackContext() as rollback: deep_path = os.path.join(TMP_DIR, 'deep-scan') subdir_path = os.path.join(deep_path, 'isos') if not os.path.exists(subdir_path): os.makedirs(subdir_path) ubuntu_iso = os.path.join(deep_path, 'ubuntu12.04.iso') sles_iso = os.path.join(subdir_path, 'sles10.iso') iso_gen.construct_fake_iso(ubuntu_iso, True, '12.04', 'ubuntu') iso_gen.construct_fake_iso(sles_iso, True, '10', 'sles') args = { 'name': 'kimchi-scanning-pool', 'path': deep_path, 'type': 'kimchi-iso' } inst.storagepools_create(args) rollback.prependDefer(shutil.rmtree, deep_path) rollback.prependDefer(shutil.rmtree, args['path']) rollback.prependDefer(inst.storagepool_deactivate, args['name']) time.sleep(1) volumes = inst.storagevolumes_get_list(args['name']) self.assertEquals(len(volumes), 2)