diff --git a/core/sessions.py b/core/sessions.py index d873dafa..672c2b27 100644 --- a/core/sessions.py +++ b/core/sessions.py @@ -15,7 +15,6 @@ from core.logger import log from core.exceptions import SessionException -from vmpool.endpoint import delete_vm from core.video import VNCVideoHelper from flask import current_app @@ -121,11 +120,9 @@ def delete(self, message=""): self.save() current_app.sessions.remove(self) - if hasattr(self, "endpoint"): + if hasattr(self, "endpoint") and self.endpoint: log.info("Deleting VM for session: %s" % self.id) self.endpoint.delete() - else: - delete_vm(self.endpoint_name) log.info("Session %s deleted. %s" % (self.id, message)) def succeed(self): diff --git a/tests/unit/test_server.py b/tests/unit/test_server.py index 7da9d6a7..01d4aac1 100644 --- a/tests/unit/test_server.py +++ b/tests/unit/test_server.py @@ -11,7 +11,7 @@ from tests.unit.helpers import server_is_up, server_is_down, \ new_session_request, get_session_request, delete_session_request, \ vmmaster_label, run_script, request_with_drop, BaseTestCase, \ - set_primary_key, DatabaseMock + set_primary_key, wait_for, DatabaseMock from nose.twistedtools import reactor @@ -473,6 +473,80 @@ def test_req_closed_when_request_append_to_queue(self): self.assertEqual(0, self.pool.count()) + def test_req_closed_when_platform_queued(self): + """ + - wait until platform is queued + - check queue state + - drop request while platform is queued + Expected: platform no more in queue + """ + import vmpool.virtual_machines_pool as vmp + with patch.object(vmp, 'pool', Mock()) as p: + p.has = Mock(return_value=False) + p.can_produce = Mock(return_value=True) + + from vmpool.vmqueue import q + + def wait_for_platform_in_queue(): + wait_for(lambda: q, timeout=2) + self.assertEqual(len(q), 1) + self.assertEqual( + q[0].dc, self.desired_caps["desiredCapabilities"] + ) + + request_with_drop( + self.address, self.desired_caps, wait_for_platform_in_queue + ) + wait_for(lambda: not q, timeout=2) + self.assertEqual(len(q), 0) + + @patch.multiple( + "vmpool.clone.KVMClone", + clone_origin=Mock(), + define_clone=Mock(), + start_virtual_machine=Mock(), + drive_path=Mock() + ) + def test_req_closed_when_vm_is_spawning(self): + """ + - waiting for clone spawning to begin + - drop request while vm is spawning + Expected: queue is empty, vm spawned and then deleted + """ + + vm_mock = Mock() + vm_mock.delete = Mock() + + def just_sleep(*args, **kwargs): + time.sleep(2) + return vm_mock + + from vmpool.virtual_machines_pool import pool + with patch.object( + pool, 'has', Mock(return_value=False) + ), patch.object( + pool, 'can_produce', Mock(return_value=True) + ), patch( + 'vmpool.platforms.KVMOrigin.make_clone', + Mock(side_effect=just_sleep) + ) as make_clone: + from vmpool.vmqueue import q + + def wait_for_vm_start_tp_spawn(): + wait_for(lambda: make_clone.called, timeout=2) + self.assertTrue(make_clone.called) + self.assertEqual(len(q), 1) + + request_with_drop( + self.address, self.desired_caps, wait_for_vm_start_tp_spawn + ) + + wait_for(lambda: not q, timeout=2) + self.assertEqual(len(q), 0) + + wait_for(lambda: vm_mock.delete.called) + vm_mock.delete.assert_any_call() + class TestServerShutdown(BaseTestServer): def setUp(self): diff --git a/vmpool/clone.py b/vmpool/clone.py index 00ec7342..de9a5460 100644 --- a/vmpool/clone.py +++ b/vmpool/clone.py @@ -97,12 +97,12 @@ def __init__(self, origin, prefix): def delete(self): log.info("Deleting kvm clone: {}".format(self.name)) self.ready = False - utils.delete_file(self.drive_path) utils.delete_file(self.dumpxml_file) try: domain = self.conn.lookupByName(self.name) - domain.destroy() + if domain.isActive(): + domain.destroy() domain.undefine() except libvirtError: # not running @@ -112,7 +112,6 @@ def delete(self): except ValueError, e: log.warning(e) pass - pool.remove_vm(self) VirtualMachine.delete(self) @@ -258,7 +257,6 @@ def get_ip(self): @threaded_wait def _wait_for_activated_service(self, method=None): - from time import sleep config_create_check_retry_count, config_create_check_pause = \ config.VM_CREATE_CHECK_ATTEMPTS, config.VM_CREATE_CHECK_PAUSE config_ping_retry_count, config_ping_timeout = \ @@ -286,7 +284,7 @@ def _wait_for_activated_service(self, method=None): "check this VM" % (self.name, p)) create_check_retry += 1 - sleep(config_create_check_pause) + time.sleep(config_create_check_pause) elif self.vm_has_created(): if method is not None: diff --git a/vmpool/endpoint.py b/vmpool/endpoint.py index 1440b5c4..7fae8ae7 100644 --- a/vmpool/endpoint.py +++ b/vmpool/endpoint.py @@ -27,30 +27,38 @@ def new_vm(desired_caps): platform = platform.encode('utf-8') if not platform: - raise CreationException('Platform parameter for ' - 'new endpoint not found in dc') + raise CreationException( + 'Platform parameter for new endpoint not found in dc' + ) if not Platforms.check_platform(platform): raise PlatformException('No such platform %s' % platform) delayed_vm = q.enqueue(desired_caps) + yield delayed_vm for condition in generator_wait_for( - lambda: delayed_vm.vm, timeout=config.GET_VM_TIMEOUT): + lambda: delayed_vm.vm, timeout=config.GET_VM_TIMEOUT + ): yield delayed_vm if not delayed_vm.vm: - raise CreationException("Сouldn't create vm with platform %s" % platform) + raise CreationException( + "Timeout while waiting for vm with platform %s" % platform + ) yield delayed_vm.vm for condition in generator_wait_for( - lambda: delayed_vm.vm.ready, timeout=config.GET_VM_TIMEOUT): + lambda: delayed_vm.vm.ready, timeout=config.GET_VM_TIMEOUT + ): yield delayed_vm.vm if not delayed_vm.vm.ready: - raise CreationException('Timeout while building vm %s ' - '(platform: %s)' % (delayed_vm.vm.id, platform)) + raise CreationException( + 'Timeout while building vm %s (platform: %s)' % + (delayed_vm.vm.id, platform) + ) log.info('Got vm for request with params: %s' % delayed_vm.vm.info) yield delayed_vm.vm @@ -69,4 +77,3 @@ def delete_vm(endpoint_name): else: msg = "Vm %s not found in pool or vm is busy" % endpoint_name log.info(msg) - diff --git a/vmpool/virtual_machines_pool.py b/vmpool/virtual_machines_pool.py index cfb1a5b1..b40beb27 100644 --- a/vmpool/virtual_machines_pool.py +++ b/vmpool/virtual_machines_pool.py @@ -59,8 +59,10 @@ def count(cls): @classmethod def can_produce(cls, platform): if cls.count() >= Platforms.can_produce(platform): - log.info('Can\'t produce new virtual machine with platform %s: ' - 'not enough Instances resources' % platform) + log.debug( + 'Can\'t produce new virtual machine with platform %s: ' + 'not enough Instances resources' % platform + ) return False else: return True @@ -77,9 +79,11 @@ def get_by_platform(cls, platform=None): if platform: for vm in sorted(cls.pool, key=lambda v: v.created, reverse=True): - log.info("Got VM %s (ip=%s, ready=%s, checking=" - "%s)" % (vm.name, vm.ip, vm.ready, vm.checking)) if vm.platform == platform and vm.ready and not vm.checking: + log.info( + "Got VM %s (ip=%s, ready=%s, checking=%s)" % + (vm.name, vm.ip, vm.ready, vm.checking) + ) if vm.ping_vm(): cls.pool.remove(vm) cls.using.append(vm) diff --git a/vmpool/vmqueue.py b/vmpool/vmqueue.py index c7e58891..7ce7e2ef 100644 --- a/vmpool/vmqueue.py +++ b/vmpool/vmqueue.py @@ -63,6 +63,10 @@ def run(self): try: self.queue.dequeue(delayed_vm) except QueueItemNotFound: + log.info( + "VM %s (%s) is no longer required" % + (vm.name, vm.ip) + ) vm.delete() else: delayed_vm.vm = vm