diff --git a/env-template b/env-template index 8f6ac2b..ed01240 100644 --- a/env-template +++ b/env-template @@ -43,6 +43,11 @@ OS_APPLICATION_CREDENTIAL_SECRET= OS_SECGROUPS=bumblebee OS_KEYNAME=bumblebee +# Type of desktop console server that is used by the Guacamole server: +# 'openstack_hypervisor' uses the builtin VNC server of the OpenStack hypervisor +# 'instance_builtin' uses an RDP server running within a created OpenStack desktop instance +OS_CONSOLE_SERVER=instance_builtin + ### Guacamole OpenID Connect integration config # Guacamole requires a non-confidential OIDC client with implicit flow enabled. diff --git a/researcher_workspace/local_settings_template.py b/researcher_workspace/local_settings_template.py index ae65474..dc60113 100644 --- a/researcher_workspace/local_settings_template.py +++ b/researcher_workspace/local_settings_template.py @@ -39,6 +39,11 @@ OS_NETWORK = "" # ¡Change! OS_SECGROUPS = [] # ¡Change! +# Type of desktop console server that is used by the Guacamole server: +# 'openstack_hypervisor' uses the builtin VNC server of the OpenStack hypervisor +# 'instance_builtin' uses an RDP server running within a created OpenStack desktop instance +OS_CONSOLE_SERVER = 'instance_builtin' + ### Researcher Desktop Settings # Banner label on site only visible by users with is_superuser=True diff --git a/researcher_workspace/settings.py b/researcher_workspace/settings.py index eea3cb2..23650c1 100644 --- a/researcher_workspace/settings.py +++ b/researcher_workspace/settings.py @@ -162,6 +162,11 @@ def get_setting(setting, default=None, required=False): OS_PROJECT_ID = get_setting('OS_PROJECT_ID', '') +# Type of desktop console server that is used by the Guacamole server: +# 'openstack_hypervisor' uses the builtin VNC server of the OpenStack hypervisor +# 'instance_builtin' uses an RDP server running within a created OpenStack desktop instance +OS_CONSOLE_SERVER = get_setting('OS_CONSOLE_SERVER', 'instance_builtin') + AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend', 'researcher_workspace.auth.NectarAuthBackend', diff --git a/vm_manager/migrations/0016_add_console_addr_port.py b/vm_manager/migrations/0016_add_console_addr_port.py new file mode 100644 index 0000000..71d4ae3 --- /dev/null +++ b/vm_manager/migrations/0016_add_console_addr_port.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.16 on 2022-12-19 09:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('vm_manager', '0015_backfill_backup_expiries'), + ] + + operations = [ + migrations.AddField( + model_name='instance', + name='console_addr', + field=models.GenericIPAddressField(blank=True, null=True), + ), + migrations.AddField( + model_name='instance', + name='console_port', + field=models.PositiveIntegerField(blank=True, null=True), + ), + ] diff --git a/vm_manager/models.py b/vm_manager/models.py index 0f1fbb9..7e1e1a9 100644 --- a/vm_manager/models.py +++ b/vm_manager/models.py @@ -317,6 +317,8 @@ def get_instance_by_untrusted_vm_id_2(self, vm_id, requesting_feature, class Instance(CloudResource): boot_volume = models.ForeignKey(Volume, on_delete=models.PROTECT, ) ip_address = models.GenericIPAddressField(null=True, blank=True) + console_addr = models.GenericIPAddressField(null=True, blank=True) + console_port = models.PositiveIntegerField(null=True, blank=True) guac_connection = models.ForeignKey(GuacamoleConnection, on_delete=models.SET_NULL, null=True, blank=True) username = models.CharField(max_length=20) @@ -335,19 +337,45 @@ def get_ip_addr(self): self.save() return self.ip_address + def get_console_addr_port(self): + if self.console_addr and self.console_port: + return self.console_addr, self.console_port + else: + n = get_nectar() + console_addr, console_port = n.get_console_connection(self.id) + self.console_addr = console_addr + self.console_port = console_port + self.save() + return self.console_addr, self.console_port + + def get_console_protocol(self): + n = get_nectar() + return n.get_console_protocol() + def create_guac_connection(self): + # save console connection information of OpenStack instance + console_addr, console_port = self.get_console_addr_port() + console_protocol = self.get_console_protocol() + + # prepare Guacamole connection parameters params = [ - ('hostname', self.get_ip_addr()), - ('username', self.username), - ('password', self.password), - ('security', 'tls'), - ('ignore-cert', 'true'), - ('resize-method', 'display-update'), - ('enable-drive', 'true'), - ('drive-path', f'/var/lib/guacd/shared-drive/{self.id}'), - ('create-drive-path', 'true'), + ('hostname', console_addr), + ('port', console_port) ] + if console_protocol == 'rdp': + # RDP connections need additional Guacamole connection parameters + params.extend([ + ('username', self.username), + ('password', self.password), + ('security', 'tls'), + ('ignore-cert', 'true'), + ('resize-method', 'display-update'), + ('enable-drive', 'true'), + ('drive-path', f'/var/lib/guacd/shared-drive/{self.id}'), + ('create-drive-path', 'true') + ]) + for k, v in params: gcp, created = GuacamoleConnectionParameter.objects.get_or_create( connection=self.guac_connection, diff --git a/vm_manager/tests/fakes.py b/vm_manager/tests/fakes.py index 1ae191d..4dc8ec3 100644 --- a/vm_manager/tests/fakes.py +++ b/vm_manager/tests/fakes.py @@ -2,6 +2,7 @@ import uuid from vm_manager.tests.common import UUID_1, UUID_2 +from vm_manager.utils.utils import Nectar class Fake(object): @@ -40,7 +41,7 @@ class FakeServer(Fake): ] -class FakeNectar(object): +class FakeNectar(Nectar): def __init__(self): self.nova = Mock() self.nova.flavors.list = Mock(return_value=FLAVORS) @@ -55,3 +56,9 @@ def __init__(self): self.cinder.volumes.list = Mock(return_value=VOLUMES) self.cinder.volumes.create = Mock( return_value=FakeVolume(id=UUID_1)) + + def get_console_connection(self, server_id): + pass + + def get_console_protocol(self): + pass \ No newline at end of file diff --git a/vm_manager/tests/unit/test_models.py b/vm_manager/tests/unit/test_models.py index 7c5180d..95074bd 100644 --- a/vm_manager/tests/unit/test_models.py +++ b/vm_manager/tests/unit/test_models.py @@ -16,7 +16,7 @@ ResizeFactory, VMStatusFactory from vm_manager.tests.fakes import Fake, FakeNectar from vm_manager.constants import ERROR, VM_DELETED -from vm_manager.utils.utils import get_nectar +from vm_manager.utils.utils import get_nectar, NectarFactory from vm_manager.models import Instance, Volume, Resize, VMStatus, \ _create_hostname_id @@ -61,9 +61,9 @@ def test_superclass_methods(self): fake_volume = self.make_volume() self.do_superclass_method_tests(fake_volume) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.models._create_hostname_id') - def test_volume_save(self, mock_gen): + def test_volume_save(self, mock_gen, mock_cn): mock_gen.return_value = "fnord" id = uuid.uuid4() volume = self.make_volume(id=id) @@ -135,8 +135,8 @@ def test_superclass_methods(self): self.do_superclass_method_tests(fake_instance) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) - def test_get_ip_addr(self): + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_get_ip_addr(self, mock_cn): fake_volume = self.make_volume() fake_instance = InstanceFactory.create( id=uuid.uuid4(), user=self.user, boot_volume=fake_volume) @@ -165,8 +165,8 @@ def test_get_ip_addr(self): self.assertEqual(dummy_ip, ip) self.assertIsNotNone(fake_instance.ip_address) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) - def test_get_status(self): + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_get_status(self, mock_cn): fake = get_nectar() fake.nova.servers.get.return_value = Fake(status='testing') fake.nova.servers.get.reset_mock() @@ -180,13 +180,18 @@ def test_get_status(self): fake.nova.servers.get.assert_called_once_with(fake_instance.id) self.assertEqual('testing', status) - def test_create_guac_connection(self): + @patch.object(FakeNectar, 'get_console_protocol', return_value="rdp") + @patch.object(FakeNectar, 'get_console_connection', + return_value=("192.168.122.20", 5901)) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_create_guac_connection(self, mock_cn, mock_conn, mock_proto): fake_volume = self.make_volume() fake_guac_connection = GuacamoleConnectionFactory.create() fake_instance = InstanceFactory.create( id=uuid.uuid4(), user=self.user, boot_volume=fake_volume, guac_connection=fake_guac_connection, - ip_address="10.0.0.1") + ip_address="10.0.0.1", console_addr="192.168.122.20", + console_port=5901) with self.assertRaises(GuacamoleEntity.DoesNotExist): self.assertIsNone(GuacamoleEntity.objects.get( @@ -200,7 +205,7 @@ def test_create_guac_connection(self): entity = GuacamoleEntity.objects.get(name=self.user.username) self.assertIsNotNone(entity) - self.assertEqual(9, + self.assertEqual(10, GuacamoleConnectionParameter.objects.filter( connection=fake_guac_connection).count()) self.assertEqual(1, @@ -265,8 +270,8 @@ def test_get_instance_by_ip(self): f"with ip_address={ip_address}", str(cm.exception)) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) - def test_get_instance_by_ip_with_lookup(self): + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_get_instance_by_ip_with_lookup(self, mock_cn): ip_address = '10.0.0.3' ip_address_2 = '10.0.0.4' diff --git a/vm_manager/tests/unit/test_views.py b/vm_manager/tests/unit/test_views.py index f8158dd..db1aa4a 100644 --- a/vm_manager/tests/unit/test_views.py +++ b/vm_manager/tests/unit/test_views.py @@ -25,7 +25,7 @@ from vm_manager.models import VMStatus, Instance, Volume, Resize, Expiration, \ EXP_INITIAL, EXP_FIRST_WARNING, EXP_EXPIRING, EXP_EXPIRY_COMPLETED, \ EXP_EXPIRY_FAILED_RETRYABLE -from vm_manager.utils.utils import get_nectar, after_time +from vm_manager.utils.utils import get_nectar, after_time, NectarFactory from vm_manager.views import launch_vm_worker, delete_vm_worker, \ shelve_vm_worker, unshelve_vm_worker, reboot_vm_worker, \ supersize_vm_worker, downsize_vm_worker @@ -408,8 +408,8 @@ def test_downsize_vm(self, mock_rq): self.assertEqual(0, vm_status.status_progress) self.assertTrue(now < vm_status.wait_time) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) - def test_get_vm_state(self): + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_get_vm_state(self, mock_cn): self.build_existing_vm(None) self.assertEqual((NO_VM, "No VM", None), get_vm_state(self.vm_status, @@ -486,10 +486,10 @@ def test_get_vm_state(self): get_vm_state(self.vm_status, self.user, self.UBUNTU)) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.models.Instance.get_url') @patch('vm_manager.views.InstanceExpiryPolicy') - def test_get_vm_state_2(self, mock_policy_class, mock_get_url): + def test_get_vm_state_2(self, mock_policy_class, mock_get_url, mock_cn): url = "https://foo/bar" mock_get_url.return_value = url @@ -530,10 +530,10 @@ def test_get_vm_state_2(self, mock_policy_class, mock_get_url): instance.error_message) self.assertIsNone(instance.boot_volume.error_message) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.models.Instance.get_url') @patch('vm_manager.views.BoostExpiryPolicy') - def test_get_vm_state_3(self, mock_policy_class, mock_get_url): + def test_get_vm_state_3(self, mock_policy_class, mock_get_url, mock_cn): url = "https://foo/bar" mock_get_url.return_value = url @@ -566,9 +566,9 @@ def test_get_vm_state_3(self, mock_policy_class, mock_get_url): self.instance.id), get_vm_state(self.vm_status, self.user, self.UBUNTU)) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.views.VolumeExpiryPolicy') - def test_get_vm_state_4(self, mock_policy_class): + def test_get_vm_state_4(self, mock_policy_class, mock_cn): # Not testing the expiration policy decisions mock_policy = Mock() diff --git a/vm_manager/tests/unit/vm_functions/test_admin_functionality.py b/vm_manager/tests/unit/vm_functions/test_admin_functionality.py index 698b8e5..8c9c5c5 100644 --- a/vm_manager/tests/unit/vm_functions/test_admin_functionality.py +++ b/vm_manager/tests/unit/vm_functions/test_admin_functionality.py @@ -3,11 +3,13 @@ from django.utils.timezone import utc +from vm_manager.tests.fakes import FakeNectar from vm_manager.tests.unit.vm_functions.base import VMFunctionTestBase from vm_manager.constants import VM_OKAY, VM_DELETED, VM_WAITING, VM_SUPERSIZED from vm_manager.models import VMStatus, Volume, Instance from vm_manager.tests.factories import ResizeFactory +from vm_manager.utils.utils import NectarFactory from vm_manager.vm_functions.admin_functionality import \ admin_shelve_instance, admin_delete_instance_and_volume, \ admin_delete_volume, admin_archive_volume, \ @@ -67,7 +69,9 @@ def test_admin_delete_instance_and_volume_2(self, mock_delete, mock_rq): @patch('vm_manager.vm_functions.admin_functionality.django_rq') @patch('vm_manager.vm_functions.admin_functionality.archive_volume_worker') - def test_admin_archive_instance_and_volume(self, mock_archive, mock_rq): + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_admin_archive_instance_and_volume(self, mock_cn, mock_archive, + mock_rq): mock_queue = Mock() mock_rq.get_queue.return_value = mock_queue fake_volume, fake_instance, fake_vmstatus = \ @@ -90,7 +94,9 @@ def test_admin_archive_instance_and_volume(self, mock_archive, mock_rq): @patch('vm_manager.vm_functions.admin_functionality.django_rq') @patch('vm_manager.vm_functions.admin_functionality.archive_volume_worker') - def test_admin_archive_instance_and_volume_2(self, mock_archive, mock_rq): + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_admin_archive_instance_and_volume_2(self, mock_cn, mock_archive, + mock_rq): mock_queue = Mock() mock_rq.get_queue.return_value = mock_queue fake_volume, fake_instance, fake_vmstatus = \ @@ -112,7 +118,8 @@ def test_admin_archive_instance_and_volume_2(self, mock_archive, mock_rq): @patch('vm_manager.vm_functions.admin_functionality.django_rq') @patch('vm_manager.vm_functions.admin_functionality.delete_volume') - def test_admin_delete_volume(self, mock_delete, mock_rq): + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_admin_delete_volume(self, mock_cn, mock_delete, mock_rq): mock_queue = Mock() mock_rq.get_queue.return_value = mock_queue fake_volume, fake_instance, fake_vmstatus = \ @@ -132,7 +139,8 @@ def test_admin_delete_volume(self, mock_delete, mock_rq): @patch('vm_manager.vm_functions.admin_functionality.django_rq') @patch('vm_manager.vm_functions.admin_functionality.archive_volume_worker') - def test_admin_archive_volume(self, mock_archive, mock_rq): + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_admin_archive_volume(self, mock_cn, mock_archive, mock_rq): mock_queue = Mock() mock_rq.get_queue.return_value = mock_queue fake_volume, fake_instance, fake_vmstatus = \ @@ -151,7 +159,8 @@ def test_admin_archive_volume(self, mock_archive, mock_rq): @patch('vm_manager.vm_functions.admin_functionality.django_rq') @patch('vm_manager.vm_functions.admin_functionality.logger') - def test_admin_shelve_instance(self, mock_logger, mock_rq): + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_admin_shelve_instance(self, mock_cn, mock_logger, mock_rq): mock_queue = Mock() mock_rq.get_queue.return_value = mock_queue fake_volume, fake_instance, fake_vmstatus = \ @@ -174,7 +183,8 @@ def test_admin_shelve_instance(self, mock_logger, mock_rq): @patch('vm_manager.vm_functions.admin_functionality.django_rq') @patch('vm_manager.vm_functions.admin_functionality.logger') - def test_admin_downsize_instance(self, mock_logger, mock_rq): + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) + def test_admin_downsize_instance(self, mock_cn, mock_logger, mock_rq): mock_queue = Mock() mock_rq.get_queue.return_value = mock_queue fake_volume, fake_instance, fake_vmstatus = \ diff --git a/vm_manager/tests/unit/vm_functions/test_create_vm.py b/vm_manager/tests/unit/vm_functions/test_create_vm.py index 1fc750d..b4f94a4 100644 --- a/vm_manager/tests/unit/vm_functions/test_create_vm.py +++ b/vm_manager/tests/unit/vm_functions/test_create_vm.py @@ -21,7 +21,7 @@ from vm_manager.vm_functions.create_vm import launch_vm_worker, \ wait_to_create_instance, _create_volume, _create_instance, \ wait_for_instance_active, _get_source_volume_id, extend_instance -from vm_manager.utils.utils import get_nectar +from vm_manager.utils.utils import get_nectar, NectarFactory class CreateVMTests(VMFunctionTestBase): @@ -97,8 +97,9 @@ def test_launch_vm_worker_instance_deleted(self, mock_rq, mock_create): @patch('vm_manager.vm_functions.create_vm._create_instance') @patch('vm_manager.vm_functions.create_vm.django_rq') @patch('vm_manager.vm_functions.create_vm.datetime') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_wait_to_create(self, mock_get, mock_datetime, + def test_wait_to_create(self, mock_get, mock_cn, mock_datetime, mock_rq, mock_create_instance): now = datetime.now(utc) mock_datetime.now.return_value = now @@ -132,9 +133,10 @@ def test_wait_to_create(self, mock_get, mock_datetime, @patch('vm_manager.vm_functions.create_vm._create_instance') @patch('vm_manager.vm_functions.create_vm.django_rq') @patch('vm_manager.vm_functions.create_vm.datetime') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_wait_to_create_unshelve(self, mock_get, mock_datetime, mock_rq, - mock_create_instance): + def test_wait_to_create_unshelve(self, mock_get, mock_cn, mock_datetime, + mock_rq, mock_create_instance): now = datetime.now(utc) mock_datetime.now.return_value = now mock_scheduler = Mock() @@ -167,9 +169,10 @@ def test_wait_to_create_unshelve(self, mock_get, mock_datetime, mock_rq, @patch('vm_manager.vm_functions.create_vm.django_rq') @patch('vm_manager.vm_functions.create_vm._create_instance') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_wait_to_create_timeout(self, mock_get, mock_create_instance, - mock_rq): + def test_wait_to_create_timeout(self, mock_get, mock_cn, + mock_create_instance, mock_rq): fake = FakeNectar() fake_volume, _, fake_status = self.build_fake_vol_inst_status() fake.cinder.volumes.get.return_value = FakeVolume( @@ -198,8 +201,9 @@ def test_wait_to_create_timeout(self, mock_get, mock_create_instance, @patch('vm_manager.vm_functions.create_vm.django_rq') @patch('vm_manager.vm_functions.create_vm._create_instance') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_wait_to_create_poll(self, mock_get, mock_create_instance, + def test_wait_to_create_poll(self, mock_get, mock_cn, mock_create_instance, mock_rq): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler @@ -223,8 +227,10 @@ def test_wait_to_create_poll(self, mock_get, mock_create_instance, @patch('vm_manager.vm_functions.create_vm.generate_server_name') @patch('vm_manager.vm_functions.create_vm._get_source_volume_id') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_create_volume(self, mock_get_nectar, mock_get_id, mock_gen): + def test_create_volume(self, mock_get_nectar, mock_cn, mock_get_id, + mock_gen): mock_gen.return_value = "abcdef" mock_get_id.return_value = self.UBUNTU_source_volume_id fake_vm_status = VMStatusFactory.create( @@ -261,9 +267,10 @@ def test_create_volume(self, mock_get_nectar, mock_get_id, mock_gen): self.assertEqual(NO_VM, vm_status.status) self.assertEqual(25, vm_status.status_progress) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') @patch('vm_manager.vm_functions.create_vm.logger') - def test_create_volume_exists(self, mock_logger, mock_get): + def test_create_volume_exists(self, mock_logger, mock_get, mock_cn): fake_volume, _, _ = self.build_fake_vol_inst_status() fake = FakeNectar() @@ -280,8 +287,9 @@ def test_create_volume_exists(self, mock_logger, mock_get): "_create_volume. Needs manual cleanup.") fake.cinder.volumes.get.assert_called_once_with(fake_volume.id) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_create_volume_shelved(self, mock_get): + def test_create_volume_shelved(self, mock_get, mock_cn): fake_volume, _, _ = self.build_fake_vol_inst_status(status=VM_SHELVED) fake = FakeNectar() @@ -294,9 +302,10 @@ def test_create_volume_shelved(self, mock_get): _create_volume(self.user, self.UBUNTU, self.zone)) fake.cinder.volumes.get.assert_called_once_with(fake_volume.id) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') @patch('vm_manager.vm_functions.create_vm.logger') - def test_create_volume_archived(self, mock_logger, mock_get): + def test_create_volume_archived(self, mock_logger, mock_get, mock_cn): fake_volume, _, _ = self.build_fake_vol_inst_status(status=VM_SHELVED) fake = FakeNectar() @@ -313,8 +322,9 @@ def test_create_volume_archived(self, mock_logger, mock_get): mock_logger.error.assert_called_once_with( f"Cannot launch shelved volume marked as archived: {fake_volume}") + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_create_volume_missing(self, mock_get): + def test_create_volume_missing(self, mock_get, mock_cn): fake_volume, _, _ = self.build_fake_vol_inst_status() fake = FakeNectar() @@ -326,9 +336,10 @@ def test_create_volume_missing(self, mock_get): self.assertEqual(None, result) fake.cinder.volumes.get.assert_called_once_with(fake_volume.id) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') @patch('vm_manager.vm_functions.create_vm.logger') - def test_create_volume_wrong_zone(self, mock_logger, mock_get): + def test_create_volume_wrong_zone(self, mock_logger, mock_get, mock_cn): fake_volume, _, _ = self.build_fake_vol_inst_status() fake = FakeNectar() fake.cinder.volumes.get.return_value = FakeVolume( @@ -347,8 +358,9 @@ def test_create_volume_wrong_zone(self, mock_logger, mock_get): "Needs manual cleanup") fake.cinder.volumes.get.assert_called_once_with(fake_volume.id) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_create_volume_deleted(self, mock_get): + def test_create_volume_deleted(self, mock_get, mock_cn): fake_volume, _, _ = self.build_fake_vol_inst_status(status=NO_VM) new_vol_id = str(uuid.uuid4()) @@ -368,8 +380,9 @@ def test_create_volume_deleted(self, mock_get): self.assertIsNone(volume.error_message) fake.cinder.volumes.create.assert_not_called() + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_get_source_volume_id(self, mock_get): + def test_get_source_volume_id(self, mock_get, mock_cn): id = str(uuid.uuid4()) fake = get_nectar() fake.cinder.volumes.list.reset_mock() @@ -408,9 +421,12 @@ def test_get_source_volume_id(self, mock_get): @patch('vm_manager.vm_functions.create_vm.generate_server_name') @patch('vm_manager.vm_functions.create_vm.generate_password') @patch('vm_manager.vm_functions.create_vm.render_to_string') + @patch.object(FakeNectar, 'get_console_protocol', return_value="rdp") + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_create_instance(self, mock_get, mock_render, mock_gen_password, - mock_gen_server_name, mock_gen_hostname): + def test_create_instance(self, mock_get, mock_cn, mock_proto, mock_render, + mock_gen_password, mock_gen_server_name, + mock_gen_hostname): mock_gen_hostname.return_value = "mullion" mock_gen_server_name.return_value = "foobar" mock_gen_password.return_value = "secret" @@ -456,9 +472,11 @@ def test_create_instance(self, mock_get, mock_render, mock_gen_password, ) @patch('vm_manager.vm_functions.create_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') @patch('vm_manager.models.get_nectar') - def test_wait_for_active_timeout(self, mock_get, mock_get_2, mock_rq): + def test_wait_for_active_timeout(self, mock_get, mock_get_2, mock_cn, + mock_rq): fake = FakeNectar() _, fake_instance, fake_status = self.build_fake_vol_inst_status() fake.nova.servers.get.return_value = FakeServer( @@ -481,9 +499,10 @@ def test_wait_for_active_timeout(self, mock_get, mock_get_2, mock_rq): mock_rq.get_scheduler.assert_not_called() @patch('vm_manager.vm_functions.create_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.create_vm.get_nectar') @patch('vm_manager.models.get_nectar') - def test_wait_for_active_poll(self, mock_get, mock_get_2, mock_rq): + def test_wait_for_active_poll(self, mock_get, mock_get_2, mock_cn, mock_rq): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake = FakeNectar() @@ -503,9 +522,11 @@ def test_wait_for_active_poll(self, mock_get, mock_get_2, mock_rq): self.user, self.UBUNTU, fake_instance, start) @patch('vm_manager.vm_functions.create_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.models.get_nectar') @patch('vm_manager.vm_functions.create_vm.get_nectar') - def test_wait_for_active_success(self, mock_get, mock_get_2, mock_rq): + def test_wait_for_active_success(self, mock_get, mock_get_2, mock_cn, + mock_rq): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake = FakeNectar() diff --git a/vm_manager/tests/unit/vm_functions/test_delete_vm.py b/vm_manager/tests/unit/vm_functions/test_delete_vm.py index c03ad6d..9c90cb1 100644 --- a/vm_manager/tests/unit/vm_functions/test_delete_vm.py +++ b/vm_manager/tests/unit/vm_functions/test_delete_vm.py @@ -29,15 +29,15 @@ _dispose_volume_once_instance_is_deleted, delete_volume, \ archive_volume_worker, wait_for_backup, delete_backup_worker, \ _wait_until_backup_is_deleted, _wait_until_volume_is_deleted -from vm_manager.utils.utils import get_nectar +from vm_manager.utils.utils import get_nectar, NectarFactory class DeleteVMTests(VMFunctionTestBase): - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.django_rq') @patch('vm_manager.vm_functions.delete_vm.logger') - def test_delete_vm_worker(self, mock_logger, mock_rq): + def test_delete_vm_worker(self, mock_logger, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler _, fake_instance = self.build_fake_vol_instance(ip_address='10.0.0.99') @@ -68,10 +68,11 @@ def test_delete_vm_worker(self, mock_logger, mock_rq): mock_logger.info.assert_called_once_with( f"About to delete {fake_instance}") - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.django_rq') @patch('vm_manager.vm_functions.delete_vm.logger') - def test_delete_vm_worker_missing_instance(self, mock_logger, mock_rq): + def test_delete_vm_worker_missing_instance(self, mock_logger, mock_rq, + mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler _, fake_instance = self.build_fake_vol_instance(ip_address='10.0.0.99') @@ -110,10 +111,11 @@ def test_delete_vm_worker_missing_instance(self, mock_logger, mock_rq): self.assertEqual("Nova instance is missing", instance.error_message) self.assertIsNotNone(instance.marked_for_deletion) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.django_rq') @patch('vm_manager.vm_functions.delete_vm.logger') - def test_delete_vm_worker_already_stopped(self, mock_logger, mock_rq): + def test_delete_vm_worker_already_stopped(self, mock_logger, mock_rq, + mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler _, fake_instance = self.build_fake_vol_instance(ip_address='10.0.0.99') @@ -148,10 +150,10 @@ def test_delete_vm_worker_already_stopped(self, mock_logger, mock_rq): call(f"{instance} already shutdown in Nova."), ]) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.django_rq') @patch('vm_manager.vm_functions.delete_vm.logger') - def test_delete_vm_worker_wrong_state(self, mock_logger, mock_rq): + def test_delete_vm_worker_wrong_state(self, mock_logger, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler _, fake_instance = self.build_fake_vol_instance(ip_address='10.0.0.99') @@ -180,11 +182,12 @@ def test_delete_vm_worker_wrong_state(self, mock_logger, mock_rq): self.assertEqual(f"Nova instance state is {RESCUE}", instance.error_message) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.models.get_nectar') @patch('vm_manager.vm_functions.delete_vm.django_rq') @patch('vm_manager.vm_functions.delete_vm.logger') def test_check_instance_shutoff(self, mock_logger, mock_rq, - mock_get_nectar): + mock_get_nectar, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler _, fake_instance = self.build_fake_vol_instance(ip_address='10.0.0.99') @@ -210,12 +213,13 @@ def test_check_instance_shutoff(self, mock_logger, mock_rq, _check_instance_is_shutoff_and_delete, fake_instance, 0, funky, funky_args) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.models.get_nectar') @patch('vm_manager.vm_functions.delete_vm.django_rq') @patch('vm_manager.vm_functions.delete_vm.logger') @patch('vm_manager.vm_functions.delete_vm.delete_instance') def test_check_instance_shutoff_2(self, mock_worker, mock_logger, - mock_rq, mock_get_nectar): + mock_rq, mock_get_nectar, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler _, fake_instance, fake_status = self.build_fake_vol_inst_status( @@ -244,12 +248,13 @@ def test_check_instance_shutoff_2(self, mock_worker, mock_logger, self.assertEqual(66, updated_status.status_progress) self.assertIsNotNone(updated_status.status_message) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.models.get_nectar') @patch('vm_manager.vm_functions.delete_vm.django_rq') @patch('vm_manager.vm_functions.delete_vm.logger') @patch('vm_manager.vm_functions.delete_vm.delete_instance') def test_check_instance_shutoff_3(self, mock_worker, mock_logger, - mock_rq, mock_get_nectar): + mock_rq, mock_get_nectar, mock_cn): # This is the case where there is no VMStatus ... mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler @@ -275,9 +280,10 @@ def test_check_instance_shutoff_3(self, mock_worker, mock_logger, timedelta(seconds=INSTANCE_DELETION_RETRY_WAIT_TIME), funky, *funky_args) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') @patch('vm_manager.vm_functions.delete_vm.logger') - def test_delete_instance(self, mock_logger, mock_get_nectar): + def test_delete_instance(self, mock_logger, mock_get_nectar, mock_cn): fake_nectar = FakeNectar() mock_get_nectar.return_value = fake_nectar @@ -289,9 +295,10 @@ def test_delete_instance(self, mock_logger, mock_get_nectar): mock_logger.info.assert_called_once_with( f"Instructed Nova to delete {fake_instance}") + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') @patch('vm_manager.vm_functions.delete_vm.logger') - def test_delete_instance_2(self, mock_logger, mock_get_nectar): + def test_delete_instance_2(self, mock_logger, mock_get_nectar, mock_cn): fake_nectar = FakeNectar() mock_get_nectar.return_value = fake_nectar fake_nectar.nova.servers.delete.side_effect = \ @@ -305,9 +312,10 @@ def test_delete_instance_2(self, mock_logger, mock_get_nectar): mock_logger.info.assert_called_once_with( f"{fake_instance} already deleted") + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') @patch('vm_manager.vm_functions.delete_vm.logger') - def test_delete_instance_3(self, mock_logger, mock_get_nectar): + def test_delete_instance_3(self, mock_logger, mock_get_nectar, mock_cn): fake_nectar = FakeNectar() mock_get_nectar.return_value = fake_nectar fake_nectar.nova.servers.delete.side_effect = \ @@ -489,8 +497,9 @@ def test_delete_volume(self, mock_get_nectar): volume = Volume.objects.get(pk=fake_volume.pk) self.assertIsNotNone(volume.deleted) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_delete_volume_missing(self, mock_get_nectar): + def test_delete_volume_missing(self, mock_get_nectar, mock_cn): fake_volume = self.build_fake_volume() @@ -506,8 +515,9 @@ def test_delete_volume_missing(self, mock_get_nectar): volume = Volume.objects.get(pk=fake_volume.pk) self.assertIsNotNone(volume.deleted) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_delete_volume_failed(self, mock_get_nectar): + def test_delete_volume_failed(self, mock_get_nectar, mock_cn): fake_volume = self.build_fake_volume() @@ -527,11 +537,12 @@ def test_delete_volume_failed(self, mock_get_nectar): class ArchiveVMTests(VMFunctionTestBase): @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') @patch('vm_manager.vm_functions.delete_vm.logger') @patch('vm_manager.utils.utils.datetime') def test_archive_volume_worker(self, mock_datetime, mock_logger, - mock_get, mock_rq): + mock_get, mock_cn, mock_rq): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_volume, _, fake_vm_status = self.build_fake_vol_inst_status( @@ -664,8 +675,9 @@ def test_archive_volume_worker_wrong_state( @patch('vm_manager.vm_functions.delete_vm.django_rq') @patch('vm_manager.vm_functions.delete_vm.logger') @patch('vm_manager.vm_functions.delete_vm.delete_volume') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_wait_for_backup(self, mock_get, mock_delete, + def test_wait_for_backup(self, mock_get, mock_cn, mock_delete, mock_logger, mock_rq): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler @@ -730,8 +742,9 @@ def test_wait_for_backup(self, mock_get, mock_delete, mock_rq.get_scheduler.assert_not_called() @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_delete_backup(self, mock_get_nectar, mock_rq): + def test_delete_backup(self, mock_get_nectar, mock_cn, mock_rq): '''Backup deletion starts successfully ''' fake_volume, backup_id = self.build_fake_volume_with_backup() @@ -752,8 +765,9 @@ def test_delete_backup(self, mock_get_nectar, mock_rq): fake_volume, BACKUP_DELETION_RETRY_COUNT) @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_delete_backup_missing(self, mock_get_nectar, mock_rq): + def test_delete_backup_missing(self, mock_get_nectar, mock_cn, mock_rq): '''Backup has already been deleted ''' @@ -771,8 +785,9 @@ def test_delete_backup_missing(self, mock_get_nectar, mock_rq): mock_rq.get_scheduler.assert_not_called() @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_delete_backup_failed(self, mock_get_nectar, mock_rq): + def test_delete_backup_failed(self, mock_get_nectar, mock_cn, mock_rq): '''Backup deletion request failed ''' @@ -790,8 +805,9 @@ def test_delete_backup_failed(self, mock_get_nectar, mock_rq): mock_rq.get_scheduler.assert_not_called() @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_wait_until_backup_deleted(self, mock_get_nectar, mock_rq): + def test_wait_until_backup_deleted(self, mock_get_nectar, mock_cn, mock_rq): '''Backup deletion still progressing ''' @@ -816,8 +832,10 @@ def test_wait_until_backup_deleted(self, mock_get_nectar, mock_rq): mock_rq.get_scheduler.assert_not_called() @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_wait_until_backup_deleted_2(self, mock_get_nectar, mock_rq): + def test_wait_until_backup_deleted_2(self, mock_get_nectar, mock_cn, + mock_rq): '''Backup deletion still progressing ''' @@ -843,8 +861,10 @@ def test_wait_until_backup_deleted_2(self, mock_get_nectar, mock_rq): fake_volume, 4) @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_wait_until_backup_deleted_3(self, mock_get_nectar, mock_rq): + def test_wait_until_backup_deleted_3(self, mock_get_nectar, mock_cn, + mock_rq): '''Backup deletion did not complete in time ''' @@ -865,10 +885,11 @@ def test_wait_until_backup_deleted_3(self, mock_get_nectar, mock_rq): mock_rq.get_scheduler.assert_not_called() @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') @patch('vm_manager.vm_functions.delete_vm.logger') def test_wait_until_backup_deleted_4(self, mock_logger, - mock_get_nectar, mock_rq): + mock_get_nectar, mock_cn, mock_rq): '''Backup get call failed ''' @@ -892,8 +913,9 @@ def test_wait_until_backup_deleted_4(self, mock_logger, mock_logger.exception.assert_called_once() @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_wait_until_volume_deleted(self, mock_get_nectar, mock_rq): + def test_wait_until_volume_deleted(self, mock_get_nectar, mock_cn, mock_rq): '''Volume deletion still progressing ''' @@ -916,8 +938,10 @@ def test_wait_until_volume_deleted(self, mock_get_nectar, mock_rq): mock_rq.get_scheduler.assert_not_called() @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_wait_until_volume_deleted_2(self, mock_get_nectar, mock_rq): + def test_wait_until_volume_deleted_2(self, mock_get_nectar, mock_cn, + mock_rq): '''Volume deletion still progressing ''' @@ -943,8 +967,10 @@ def test_wait_until_volume_deleted_2(self, mock_get_nectar, mock_rq): fake_volume, 4) @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_wait_until_volume_deleted_3(self, mock_get_nectar, mock_rq): + def test_wait_until_volume_deleted_3(self, mock_get_nectar, mock_cn, + mock_rq): '''Volume deletion did not complete in time ''' @@ -963,10 +989,11 @@ def test_wait_until_volume_deleted_3(self, mock_get_nectar, mock_rq): mock_rq.get_scheduler.assert_not_called() @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') @patch('vm_manager.vm_functions.delete_vm.logger') def test_wait_until_volume_deleted_4(self, mock_logger, - mock_get_nectar, mock_rq): + mock_get_nectar, mock_cn, mock_rq): '''Volume get call failed ''' @@ -988,8 +1015,10 @@ def test_wait_until_volume_deleted_4(self, mock_logger, mock_logger.exception.assert_called_once() @patch('vm_manager.vm_functions.delete_vm.django_rq') + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.delete_vm.get_nectar') - def test_wait_until_volume_deleted_5(self, mock_get_nectar, mock_rq): + def test_wait_until_volume_deleted_5(self, mock_get_nectar, mock_cn, + mock_rq): '''Volume delete goes to bad state ''' diff --git a/vm_manager/tests/unit/vm_functions/test_other.py b/vm_manager/tests/unit/vm_functions/test_other.py index 286df31..ed6e077 100644 --- a/vm_manager/tests/unit/vm_functions/test_other.py +++ b/vm_manager/tests/unit/vm_functions/test_other.py @@ -18,15 +18,15 @@ from vm_manager.models import VMStatus, Volume, Instance from vm_manager.vm_functions.other_vm_functions import reboot_vm_worker, \ _check_power_state -from vm_manager.utils.utils import get_nectar +from vm_manager.utils.utils import get_nectar, NectarFactory class RebootVMTests(VMFunctionTestBase): - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.other_vm_functions.django_rq') @patch('vm_manager.vm_functions.other_vm_functions.logger') - def test_reboot_vm_worker(self, mock_logger, mock_rq): + def test_reboot_vm_worker(self, mock_logger, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_vol, fake_instance, fake_status = self.build_fake_vol_inst_status( @@ -58,10 +58,10 @@ def test_reboot_vm_worker(self, mock_logger, mock_rq): updated_volume = Volume.objects.get(pk=fake_vol.pk) self.assertIsNotNone(updated_volume.rebooted_at) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.other_vm_functions.django_rq') @patch('vm_manager.vm_functions.other_vm_functions.logger') - def test_reboot_vm_worker_shutdown(self, mock_logger, mock_rq): + def test_reboot_vm_worker_shutdown(self, mock_logger, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_vol, fake_instance, fake_status = self.build_fake_vol_inst_status( @@ -96,10 +96,10 @@ def test_reboot_vm_worker_shutdown(self, mock_logger, mock_rq): updated_volume = Volume.objects.get(pk=fake_vol.pk) self.assertIsNotNone(updated_volume.rebooted_at) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.other_vm_functions.django_rq') @patch('vm_manager.vm_functions.other_vm_functions.logger') - def test_reboot_vm_worker_wrong_state(self, mock_logger, mock_rq): + def test_reboot_vm_worker_wrong_state(self, mock_logger, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_vol, fake_instance, fake_status = self.build_fake_vol_inst_status( @@ -131,10 +131,10 @@ def test_reboot_vm_worker_wrong_state(self, mock_logger, mock_rq): self.assertEqual(f"Nova instance state is {RESCUE}", instance.error_message) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.other_vm_functions.django_rq') @patch('vm_manager.vm_functions.other_vm_functions.logger') - def test_reboot_vm_worker_missing(self, mock_logger, mock_rq): + def test_reboot_vm_worker_missing(self, mock_logger, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_vol, fake_instance, fake_status = self.build_fake_vol_inst_status( @@ -162,10 +162,10 @@ def test_reboot_vm_worker_missing(self, mock_logger, mock_rq): instance = Instance.objects.get(pk=fake_instance.pk) self.assertEqual("Nova instance is missing", instance.error_message) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.other_vm_functions.django_rq') @patch('vm_manager.vm_functions.other_vm_functions.logger') - def test_check_power_state(self, mock_logger, mock_rq): + def test_check_power_state(self, mock_logger, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler _, fake_instance, fake_status = self.build_fake_vol_inst_status( @@ -188,10 +188,10 @@ def test_check_power_state(self, mock_logger, mock_rq): self.assertEqual(66, updated_status.status_progress) self.assertIsNotNone(updated_status.status_message) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.other_vm_functions.django_rq') @patch('vm_manager.vm_functions.other_vm_functions.logger') - def test_check_power_state_2(self, mock_logger, mock_rq): + def test_check_power_state_2(self, mock_logger, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler _, fake_instance, fake_status = self.build_fake_vol_inst_status( @@ -218,10 +218,10 @@ def test_check_power_state_2(self, mock_logger, mock_rq): self.assertEqual(0, updated_status.status_progress) self.assertIsNone(updated_status.status_message) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.other_vm_functions.django_rq') @patch('vm_manager.vm_functions.other_vm_functions.logger') - def test_check_power_state_3(self, mock_logger, mock_rq): + def test_check_power_state_3(self, mock_logger, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler _, fake_instance, fake_status = self.build_fake_vol_inst_status( diff --git a/vm_manager/tests/unit/vm_functions/test_resize_vm.py b/vm_manager/tests/unit/vm_functions/test_resize_vm.py index 01f5a8b..88dd16c 100644 --- a/vm_manager/tests/unit/vm_functions/test_resize_vm.py +++ b/vm_manager/tests/unit/vm_functions/test_resize_vm.py @@ -20,16 +20,16 @@ from vm_manager.vm_functions.resize_vm import supersize_vm_worker, \ downsize_vm_worker, extend_boost, _resize_vm, _wait_to_confirm_resize, \ downsize_expired_vm -from vm_manager.utils.utils import get_nectar, after_time +from vm_manager.utils.utils import get_nectar, after_time, NectarFactory from vm_manager.utils.expiry import BoostExpiryPolicy class ResizeVMTests(VMFunctionTestBase): - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm._resize_vm') @patch('vm_manager.vm_functions.resize_vm.logger') - def test_supersize_vm_worker(self, mock_logger, mock_resize): + def test_supersize_vm_worker(self, mock_logger, mock_resize, mock_cn): fake_nectar = get_nectar() _, fake_instance = self.build_fake_vol_instance(ip_address='10.0.0.99') @@ -60,10 +60,11 @@ def test_supersize_vm_worker(self, mock_logger, mock_resize): self.assertTrue(abs((exp_date - resize.expiration.expires).seconds) < 2) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm._resize_vm') @patch('vm_manager.vm_functions.resize_vm.logger') - def test_supersize_vm_worker_failed(self, mock_logger, mock_resize): + def test_supersize_vm_worker_failed(self, mock_logger, mock_resize, + mock_cn): # Covers all cases where _resize_vm returns False fake_nectar = get_nectar() @@ -85,11 +86,11 @@ def test_supersize_vm_worker_failed(self, mock_logger, mock_resize): self.assertEqual( 0, Resize.objects.filter(instance=fake_instance).count()) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm._resize_vm') @patch('vm_manager.vm_functions.resize_vm.logger') - def test_downsize_vm_worker_no_resize_record( - self, mock_logger, mock_resize): + def test_downsize_vm_worker_no_resize_record(self, mock_logger, + mock_resize, mock_cn): fake_nectar = get_nectar() _, fake_instance = self.build_fake_vol_instance(ip_address='10.0.0.99') @@ -107,10 +108,10 @@ def test_downsize_vm_worker_no_resize_record( mock_logger.error_assert_called_once_with( f"Missing resize record for instance {fake_instance}") - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm._resize_vm') @patch('vm_manager.vm_functions.resize_vm.logger') - def test_downsize_vm_worker(self, mock_logger, mock_resize): + def test_downsize_vm_worker(self, mock_logger, mock_resize, mock_cn): fake_nectar = get_nectar() _, fake_instance = self.build_fake_vol_instance(ip_address='10.0.0.99') @@ -132,10 +133,11 @@ def test_downsize_vm_worker(self, mock_logger, mock_resize): resize = Resize.objects.get(pk=fake_resize.pk) self.assertIsNotNone(resize.reverted) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm._resize_vm') @patch('vm_manager.vm_functions.resize_vm.logger') - def test_downsize_vm_worker_failed(self, mock_logger, mock_resize): + def test_downsize_vm_worker_failed(self, mock_logger, mock_resize, + mock_cn): # Covers all cases where _resize_vm returns False fake_nectar = get_nectar() @@ -191,10 +193,10 @@ def test_extend(self, mock_policy_class, mock_logger): self.assertEqual(new_expiry, updated_resize.expiration.expires) mock_policy.new_expiry.assert_called_once_with(resize) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.django_rq') @patch('vm_manager.vm_functions.resize_vm.after_time') - def test_resize_vm(self, mock_after_time, mock_rq): + def test_resize_vm(self, mock_after_time, mock_rq, mock_cn): _, fake_instance, fake_vm_status = self.build_fake_vol_inst_status( status=VM_RESIZING, status_progress=0) default_flavor_id = self.UBUNTU.default_flavor.id @@ -240,9 +242,9 @@ def test_resize_vm(self, mock_after_time, mock_rq): vm_status = VMStatus.objects.get(pk=fake_vm_status.pk) self.assertEqual(33, vm_status.status_progress) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.django_rq') - def test_resize_vm_missing(self, mock_rq): + def test_resize_vm_missing(self, mock_rq, mock_cn): # The Nova instance is missing when we try to check its status. _, fake_instance, fake_vm_status = self.build_fake_vol_inst_status( status=VM_RESIZING, status_progress=0) @@ -265,9 +267,9 @@ def test_resize_vm_missing(self, mock_rq): fake_nectar.nova.servers.get.side_effect = None - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.django_rq') - def test_resize_vm_wrong_state(self, mock_rq): + def test_resize_vm_wrong_state(self, mock_rq, mock_cn): # The Nova instance has the wrong status for a resize. _, fake_instance, fake_vm_status = self.build_fake_vol_inst_status( status=VM_RESIZING, status_progress=0) @@ -293,10 +295,10 @@ def test_resize_vm_wrong_state(self, mock_rq): fake_nectar.nova.servers.get.side_effect = None - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm.django_rq') - def test_wait_to_confirm_resize(self, mock_rq, mock_logger): + def test_wait_to_confirm_resize(self, mock_rq, mock_logger, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_nectar = get_nectar() @@ -325,10 +327,10 @@ def test_wait_to_confirm_resize(self, mock_rq, mock_logger): resize = Resize.objects.get(pk=fake_resize.pk) self.assertIsNotNone(resize.reverted) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm.django_rq') - def test_wait_to_confirm_resize_2(self, mock_rq, mock_logger): + def test_wait_to_confirm_resize_2(self, mock_rq, mock_logger, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_nectar = get_nectar() @@ -358,10 +360,10 @@ def test_wait_to_confirm_resize_2(self, mock_rq, mock_logger): vm_status = VMStatus.objects.get(pk=fake_vm_status.pk) self.assertEqual(50, vm_status.status_progress) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm.django_rq') - def test_wait_to_confirm_resize_3(self, mock_rq, mock_logger): + def test_wait_to_confirm_resize_3(self, mock_rq, mock_logger, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_nectar = get_nectar() @@ -395,10 +397,10 @@ def test_wait_to_confirm_resize_3(self, mock_rq, mock_logger): self.assertEqual(error, vm_status.instance.boot_volume.error_message) self.assertEqual(50, vm_status.status_progress) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm.django_rq') - def test_wait_to_confirm_resize_4(self, mock_rq, mock_logger): + def test_wait_to_confirm_resize_4(self, mock_rq, mock_logger, mock_cn): fake_nectar = get_nectar() fake_nectar.nova.servers.get.side_effect = [ FakeServer(status=ACTIVE), @@ -424,10 +426,10 @@ def test_wait_to_confirm_resize_4(self, mock_rq, mock_logger): fake_nectar.nova.servers.get.side_effect = None - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm.django_rq') - def test_wait_to_confirm_resize_5(self, mock_rq, mock_logger): + def test_wait_to_confirm_resize_5(self, mock_rq, mock_logger, mock_cn): fake_nectar = get_nectar() fake_nectar.nova.servers.get.side_effect = None fake_nectar.nova.servers.get.return_value = FakeServer( @@ -457,10 +459,10 @@ def test_wait_to_confirm_resize_5(self, mock_rq, mock_logger): self.assertEqual(error, vm_status.instance.boot_volume.error_message) self.assertEqual(50, vm_status.status_progress) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm.django_rq') - def test_wait_to_confirm_resize_6(self, mock_rq, mock_logger): + def test_wait_to_confirm_resize_6(self, mock_rq, mock_logger, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_nectar = get_nectar() @@ -491,10 +493,10 @@ def test_wait_to_confirm_resize_6(self, mock_rq, mock_logger): resize = Resize.objects.get(pk=fake_resize.pk) self.assertIsNone(resize.reverted) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm.django_rq') - def test_wait_to_confirm_resize_6a(self, mock_rq, mock_logger): + def test_wait_to_confirm_resize_6a(self, mock_rq, mock_logger, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_nectar = get_nectar() @@ -525,10 +527,10 @@ def test_wait_to_confirm_resize_6a(self, mock_rq, mock_logger): resize = Resize.objects.get(pk=fake_resize.pk) self.assertIsNotNone(resize.reverted) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm.django_rq') - def test_wait_to_confirm_resize_7(self, mock_rq, mock_logger): + def test_wait_to_confirm_resize_7(self, mock_rq, mock_logger, mock_cn): fake_nectar = get_nectar() fake_nectar.nova.servers.get.side_effect = None fake_nectar.nova.servers.get.return_value = FakeServer(status=SHUTDOWN) @@ -556,9 +558,10 @@ def test_wait_to_confirm_resize_7(self, mock_rq, mock_logger): self.assertEqual(error, vm_status.instance.boot_volume.error_message) self.assertEqual(50, vm_status.status_progress) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm._resize_vm') - def test_downsize_expired_vm(self, mock_resize, mock_logger): + def test_downsize_expired_vm(self, mock_resize, mock_logger, mock_cn): now = datetime.now(utc) _, fake_instance, fake_vm_status = self.build_fake_vol_inst_status( @@ -581,9 +584,10 @@ def test_downsize_expired_vm(self, mock_resize, mock_logger): self.assertTrue(vm_status.wait_time >= now + timedelta( seconds=FORCED_DOWNSIZE_WAIT_SECONDS)) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm._resize_vm') - def test_downsize_expired_vm_2(self, mock_resize, mock_logger): + def test_downsize_expired_vm_2(self, mock_resize, mock_logger, mock_cn): now = datetime.now(utc) _, fake_instance, fake_vm_status = self.build_fake_vol_inst_status( @@ -597,9 +601,10 @@ def test_downsize_expired_vm_2(self, mock_resize, mock_logger): self.assertIsNone(resize.reverted) mock_resize.assert_not_called() + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.resize_vm.logger') @patch('vm_manager.vm_functions.resize_vm._resize_vm') - def test_downsize_expired_vm_3(self, mock_resize, mock_logger): + def test_downsize_expired_vm_3(self, mock_resize, mock_logger, mock_cn): now = datetime.now(utc) _, fake_instance, fake_vm_status = self.build_fake_vol_inst_status( diff --git a/vm_manager/tests/unit/vm_functions/test_shelve_vm.py b/vm_manager/tests/unit/vm_functions/test_shelve_vm.py index 94e272f..a598c66 100644 --- a/vm_manager/tests/unit/vm_functions/test_shelve_vm.py +++ b/vm_manager/tests/unit/vm_functions/test_shelve_vm.py @@ -16,7 +16,7 @@ from vm_manager.tests.common import UUID_4 from vm_manager.tests.fakes import FakeNectar, FakeServer from vm_manager.tests.unit.vm_functions.base import VMFunctionTestBase -from vm_manager.utils.utils import get_nectar +from vm_manager.utils.utils import get_nectar, NectarFactory from vm_manager.vm_functions.shelve_vm import shelve_vm_worker, \ shelve_expired_vm, _confirm_instance_deleted, \ _check_instance_is_shutoff_and_delete @@ -24,9 +24,9 @@ class ShelveVMTests(VMFunctionTestBase): - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.shelve_vm.django_rq') - def test_shelve_vm_worker_wrong_state(self, mock_rq): + def test_shelve_vm_worker_wrong_state(self, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_nectar = get_nectar() @@ -46,9 +46,9 @@ def test_shelve_vm_worker_wrong_state(self, mock_rq): instance = Instance.objects.get(pk=fake_instance.pk) self.assertIsNotNone(instance.error_message) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.shelve_vm.django_rq') - def test_shelve_vm_worker_missing(self, mock_rq): + def test_shelve_vm_worker_missing(self, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler fake_nectar = get_nectar() @@ -68,9 +68,9 @@ def test_shelve_vm_worker_missing(self, mock_rq): instance = Instance.objects.get(pk=fake_instance.pk) self.assertIsNotNone(instance.error_message) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.shelve_vm.django_rq') - def test_shelve_vm_worker_shutdown(self, mock_rq): + def test_shelve_vm_worker_shutdown(self, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler now = datetime.now(utc) @@ -102,9 +102,9 @@ def test_shelve_vm_worker_shutdown(self, mock_rq): _confirm_instance_deleted, (fake_instance, INSTANCE_DELETION_RETRY_COUNT)) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.shelve_vm.django_rq') - def test_shelve_vm_worker(self, mock_rq): + def test_shelve_vm_worker(self, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler now = datetime.now(utc) @@ -136,9 +136,9 @@ def test_shelve_vm_worker(self, mock_rq): _confirm_instance_deleted, (fake_instance, INSTANCE_DELETION_RETRY_COUNT)) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.shelve_vm.django_rq') - def test_confirm_instance_deleted(self, mock_rq): + def test_confirm_instance_deleted(self, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler now = datetime.now(utc) @@ -164,9 +164,9 @@ def test_confirm_instance_deleted(self, mock_rq): self.assertEqual(VM_SHELVED, vm_status.status) self.assertEqual(100, vm_status.status_progress) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.shelve_vm.django_rq') - def test_confirm_instance_deleted_2(self, mock_rq): + def test_confirm_instance_deleted_2(self, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler now = datetime.now(utc) @@ -189,9 +189,9 @@ def test_confirm_instance_deleted_2(self, mock_rq): instance = Instance.objects.get(pk=fake_instance.pk) self.assertIsNone(instance.deleted) - @patch('vm_manager.utils.utils.Nectar', new=FakeNectar) + @patch.object(NectarFactory, 'create', return_value=FakeNectar()) @patch('vm_manager.vm_functions.shelve_vm.django_rq') - def test_confirm_instance_deleted_3(self, mock_rq): + def test_confirm_instance_deleted_3(self, mock_rq, mock_cn): mock_scheduler = Mock() mock_rq.get_scheduler.return_value = mock_scheduler now = datetime.now(utc) diff --git a/vm_manager/utils/utils.py b/vm_manager/utils/utils.py index 4323863..756b668 100644 --- a/vm_manager/utils/utils.py +++ b/vm_manager/utils/utils.py @@ -1,5 +1,5 @@ +from abc import abstractmethod from datetime import datetime, timedelta -import logging from cinderclient import client as cinder_client from glanceclient import client as glance_client @@ -8,10 +8,48 @@ from keystoneclient import client as keystone_client from nectarallocationclient import client as allocation_client from novaclient import client as nova_client +from urllib import parse as urlparse from django.conf import settings from django.utils.crypto import get_random_string from django.utils.timezone import utc +from novaclient.v2.servers import ServerManager + + +class ServerManagerConsoleToken(ServerManager): + """ServerManagerConsoleToken + + Extended ServerManager class of the OpenStack Nova client v2 for + implementing the available Nova API endpoint '/os-console-auth-tokens'. + """ + def get_console_auth_token_info(self, console_token): + """ + Requests console connection information for specified token + """ + url = '/os-console-auth-tokens/%s' % console_token + resp, body = self.api.client.get(url) + return self.convert_into_with_meta(body, resp) + + @staticmethod + def parse_console_auth_token(access_url, console_type): + """ + Helper function to parse the console token from a console access url. + """ + def _get_url_param_value(url, param): + parsed_url = urlparse.urlparse(url).query + url_params = urlparse.parse_qs(parsed_url) + return url_params.get(param, ['']).pop() + + if console_type == 'novnc': + # URL parsing for noVNC console token URLs where the token + # parameter is encoded into the path parameter + url = _get_url_param_value(access_url, 'path') + else: + # URL parsing for all other console token URLs where the token + # parameter is encoded as usual + url = access_url + + return _get_url_param_value(url, 'token') class Nectar(object): @@ -47,16 +85,95 @@ def __init__(self): self.roles = auth_ref.role_names # Establish clients - self.nova = nova_client.Client('2', session=sess) + self.nova = nova_client.Client('2.31', session=sess) + # Patch the official Nova ServerManager with the extended one + self.nova.servers = ServerManagerConsoleToken(self.nova) self.allocation = allocation_client.Client('1', session=sess) self.keystone = keystone_client.Client('3', session=sess) self.glance = glance_client.Client('2', session=sess) self.cinder = cinder_client.Client('3', session=sess) + @abstractmethod + def get_console_connection(self, server_id): + pass + + @abstractmethod + def get_console_protocol(self): + pass + + +class NectarConsoleOpenStackHypervisor(Nectar): + """NectarConsoleOpenStackHypervisor + + Nectar OpenStack client implementing the native VNC console provided by + OpenStack (reusing the provided VNC server of the hypervisor) + """ + PROTOCOL = 'vnc' + + def get_console_connection(self, server_id): + resp = self.nova.servers.get_vnc_console(server_id, 'novnc') + console = resp.get('remote_console') + access_url = console.get('url') + console_type = console.get('type') + token = ServerManagerConsoleToken.parse_console_auth_token( + access_url, console_type) + resp = self.nova.servers.get_console_auth_token_info(token) + connection_info = resp.get('console') + return connection_info.get('host'), connection_info.get('port') + + def get_console_protocol(self): + return NectarConsoleOpenStackHypervisor.PROTOCOL + + +class NectarConsoleInstanceBuiltin(Nectar): + """NectarConsoleInstanceBuiltin + + Nectar OpenStack client implementing the RDP console built into a cloud + server's instance (reusing the integrated RDP server from a cloud instance) + """ + PROTOCOL = 'rdp' + + def get_console_connection(self, server_id): + nova_server = self.nova.servers.get(server_id) + ip_address = None + for key in nova_server.addresses: + ip_address = nova_server.addresses[key][0]['addr'] + return ip_address, 5900 + + def get_console_protocol(self): + return NectarConsoleInstanceBuiltin.PROTOCOL + + +class NectarFactory(object): + """ + Factory to register available Nectar console implementations and to create + selected console depending on the OS_CONSOLE_SERVER setting. + """ + def __init__(self): + self._nectar_creators = {} + + def register(self, console_server, creator): + self._nectar_creators[console_server] = creator + + def create(self): + console_server = settings.OS_CONSOLE_SERVER + creator = self._nectar_creators.get(console_server) + if not creator: + raise ValueError(console_server) + return creator() + + +# Register all OpenStack console server implementations +nectar_factory = NectarFactory() +nectar_factory.register('openstack_hypervisor', + NectarConsoleOpenStackHypervisor) +nectar_factory.register('instance_builtin', + NectarConsoleInstanceBuiltin) + def get_nectar(): if not hasattr(get_nectar, 'nectar'): - get_nectar.nectar = Nectar() + get_nectar.nectar = nectar_factory.create() return get_nectar.nectar diff --git a/vm_manager/vm_functions/create_vm.py b/vm_manager/vm_functions/create_vm.py index 54986f9..f13e80c 100644 --- a/vm_manager/vm_functions/create_vm.py +++ b/vm_manager/vm_functions/create_vm.py @@ -281,8 +281,10 @@ def _create_instance(user, desktop_type, volume): full_name = user.get_full_name() desktop_name = desktop_type.name connection_name = f"{full_name}'s {desktop_name} desktop" + connection_protocol = n.get_console_protocol() guac_connection = GuacamoleConnection.objects.create( - connection_name=connection_name) + connection_name=connection_name, + protocol=connection_protocol) # Create record in DB instance = Instance.objects.create(