From 0cf9f6c2dc39ff86884b670908b91e3cfd068986 Mon Sep 17 00:00:00 2001 From: Drew Thorstensen Date: Wed, 6 May 2015 15:20:54 -0500 Subject: [PATCH] Add support for PowerVM Console This change set adds support for the PowerVM VNC console. Changes the remove vterm to always be invoked. This shouldn't have a performance impact if the console is not running. Ensures proper clean up on the system running the PowerVM API. Change-Id: Ia6a5dd445410fcd5351c3528003a83ba659805bb --- .../tests/virt/powervm/test_driver.py | 14 ++++++++ nova_powervm/tests/virt/powervm/test_vm.py | 12 ++++--- nova_powervm/virt/powervm/__init__.py | 2 ++ nova_powervm/virt/powervm/driver.py | 17 ++++++++++ nova_powervm/virt/powervm/vm.py | 32 ++++++------------- 5 files changed, 51 insertions(+), 26 deletions(-) diff --git a/nova_powervm/tests/virt/powervm/test_driver.py b/nova_powervm/tests/virt/powervm/test_driver.py index 35385acb..08c8b846 100644 --- a/nova_powervm/tests/virt/powervm/test_driver.py +++ b/nova_powervm/tests/virt/powervm/test_driver.py @@ -693,6 +693,20 @@ class TestPowerVMDriver(test.TestCase): 'context', inst, None, 'HARD') self.assertEqual(pwron_count, mock_pwron.call_count) + @mock.patch('pypowervm.tasks.vterm.open_vnc_vterm') + @mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid') + def test_get_vnc_console(self, mock_uuid, mock_vterm): + # Mock response + mock_vterm.return_value = '10' + + # Invoke + inst = objects.Instance(**powervm.TEST_INSTANCE) + resp = self.drv.get_vnc_console(mock.ANY, inst) + + # Validate + self.assertEqual('127.0.0.1', resp.host) + self.assertEqual('10', resp.port) + @staticmethod def _fake_bdms(): block_device_info = { diff --git a/nova_powervm/tests/virt/powervm/test_vm.py b/nova_powervm/tests/virt/powervm/test_vm.py index 8e078a0e..39e09afe 100644 --- a/nova_powervm/tests/virt/powervm/test_vm.py +++ b/nova_powervm/tests/virt/powervm/test_vm.py @@ -160,19 +160,23 @@ class TestVM(test.TestCase): """Performs a delete LPAR test.""" vm.dlt_lpar(self.apt, '12345') self.assertEqual(1, self.apt.delete.call_count) + self.assertEqual(1, mock_vterm.call_count) - # test failure due to open vterm - resp = mock.Mock() + # Test Failure Path # build a mock response body with the expected HSCL msg + resp = mock.Mock() resp.body = 'error msg: HSCL151B more text' self.apt.delete.side_effect = pvm_exc.Error( 'Mock Error Message', response=resp) - # If failed due to vterm test close_vterm and delete are called + + # Reset counters self.apt.reset_mock() + mock_vterm.reset_mock() + self.assertRaises(pvm_exc.Error, vm.dlt_lpar, self.apt, '12345') self.assertEqual(1, mock_vterm.call_count) - self.assertEqual(2, self.apt.delete.call_count) + self.assertEqual(1, self.apt.delete.call_count) def test_build_attr(self): """Perform tests against _build_attrs.""" diff --git a/nova_powervm/virt/powervm/__init__.py b/nova_powervm/virt/powervm/__init__.py index 07cef46b..b4c6767f 100644 --- a/nova_powervm/virt/powervm/__init__.py +++ b/nova_powervm/virt/powervm/__init__.py @@ -63,6 +63,8 @@ CONF.register_opts(pvm_opts) # Options imported from other regions CONF.import_opt('host', 'nova.netconf') CONF.import_opt('my_ip', 'nova.netconf') +CONF.import_opt('vncserver_proxyclient_address', 'nova.vnc') +CONF.import_opt('vncserver_listen', 'nova.vnc') # NPIV Options will go in separate section. Only applicable if the diff --git a/nova_powervm/virt/powervm/driver.py b/nova_powervm/virt/powervm/driver.py index 2434a719..73012d53 100644 --- a/nova_powervm/virt/powervm/driver.py +++ b/nova_powervm/virt/powervm/driver.py @@ -16,6 +16,7 @@ from nova.compute import task_states from nova.compute import utils as compute_utils +from nova.console import type as console_type from nova import context as ctx from nova import exception from nova import image @@ -38,6 +39,7 @@ from pypowervm import adapter as pvm_apt from pypowervm import exceptions as pvm_exc from pypowervm.helpers import log_helper as log_hlp from pypowervm.tasks import power as pvm_pwr +from pypowervm.tasks import vterm as pvm_vterm from pypowervm import util as pvm_util from pypowervm.utils import retry as pvm_retry from pypowervm.wrappers import managed_system as pvm_ms @@ -925,6 +927,21 @@ class PowerVMDriver(driver.ComputeDriver): return [] return block_device_info.get('block_device_mapping', []) + def get_vnc_console(self, context, instance): + """Get connection info for a vnc console. + + :param context: security context + :param instance: nova.objects.instance.Instance + + :returns an instance of console.type.ConsoleVNC + """ + self._log_operation('get_vnc_console', instance) + lpar_uuid = vm.get_pvm_uuid(instance) + port = pvm_vterm.open_vnc_vterm(self.adapter, lpar_uuid, + bind_ip=CONF.vncserver_listen) + host = CONF.vncserver_proxyclient_address + return console_type.ConsoleVNC(host=host, port=port) + def _inst_dict(input_dict): """Builds a dictionary with instances as values based on the input classes. diff --git a/nova_powervm/virt/powervm/vm.py b/nova_powervm/virt/powervm/vm.py index af52c067..77a8b949 100644 --- a/nova_powervm/virt/powervm/vm.py +++ b/nova_powervm/virt/powervm/vm.py @@ -390,31 +390,19 @@ def dlt_lpar(adapter, lpar_uuid): # Attempt to delete the VM. If delete fails because of vterm # we will close the vterm and try the delete again try: - LOG.info(_LI('Deleting virtual machine. LPARID: %s') % lpar_uuid) + LOG.info(_LI('Deleting virtual machine. LPARID: %s'), lpar_uuid) + # Ensure any vterms are closed. Will no-op otherwise. + vterm.close_vterm(adapter, lpar_uuid) + + # Run the LPAR delete resp = adapter.delete(pvm_lpar.LPAR.schema_type, root_id=lpar_uuid) - LOG.info(_LI('Virtual machine delete status: %s') % resp.status) + LOG.info(_LI('Virtual machine delete status: %d'), resp.status) return resp - except pvm_exc.Error as e: - resp = e.response - if (resp.body and - any(code in str(resp.body) for code in ['HSCL151B'])): - # If this is a vterm error attempt to close vterm - try: - LOG.info(_LI('Closing virtual terminal')) - vterm.close_vterm(adapter, lpar_uuid) - # Try to delete the vm again - resp = adapter.delete(pvm_lpar.LPAR.schema_type, - root_id=lpar_uuid) - LOG.info(_LI('Virtual machine delete status: %s') - % resp.status) - return resp - except pvm_exc.Error as e: - # Fall through and raise exception - pass + except pvm_exc.Error: # Attempting to close vterm did not help so raise exception - LOG.error(_LE('Virtual machine delete failed: LPARID=%s') - % lpar_uuid) - raise e + LOG.error(_LE('Virtual machine delete failed: LPARID=%s'), + lpar_uuid) + raise def power_on(adapter, instance, host_uuid, entry=None):