From 0c3a47e57317f6efd573388c2c400a6a88745fd8 Mon Sep 17 00:00:00 2001 From: Sahid Orentino Ferdjaoui Date: Tue, 26 Aug 2014 11:00:34 +0000 Subject: [PATCH] console: add typed console objects Adds typed console objects used by virt driver API to return information about consoles. Also updates virt drivers to use them. Closes-Bug: #1361611 Change-Id: I8f6a857b88659ee30b4aa1a25ac52d7e01156a68 --- nova/compute/manager.py | 17 +++---- nova/console/type.py | 42 +++++++++++++++++ nova/tests/compute/test_compute.py | 23 ++++----- nova/tests/console/test_type.py | 59 ++++++++++++++++++++++++ nova/tests/virt/hyperv/test_hypervapi.py | 6 +-- nova/tests/virt/libvirt/test_driver.py | 4 +- nova/tests/virt/test_virt_drivers.py | 16 ++----- nova/tests/virt/xenapi/test_xenapi.py | 4 +- nova/virt/driver.py | 6 +++ nova/virt/fake.py | 21 +++++---- nova/virt/hyperv/rdpconsoleops.py | 6 +-- nova/virt/libvirt/driver.py | 6 +-- nova/virt/vmwareapi/vmops.py | 3 +- nova/virt/xenapi/vmops.py | 7 ++- 14 files changed, 162 insertions(+), 58 deletions(-) create mode 100644 nova/console/type.py create mode 100644 nova/tests/console/test_type.py diff --git a/nova/compute/manager.py b/nova/compute/manager.py index adfa40ad7f9e..2404c501c468 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -4233,9 +4233,8 @@ class ComputeManager(manager.Manager): try: # Retrieve connect info from driver, and then decorate with our # access info token - connect_info = self.driver.get_vnc_console(context, instance) - connect_info['token'] = token - connect_info['access_url'] = access_url + console = self.driver.get_vnc_console(context, instance) + connect_info = console.get_connection_info(token, access_url) except exception.InstanceNotFound: if instance['vm_state'] != vm_states.BUILDING: raise @@ -4270,9 +4269,8 @@ class ComputeManager(manager.Manager): try: # Retrieve connect info from driver, and then decorate with our # access info token - connect_info = self.driver.get_spice_console(context, instance) - connect_info['token'] = token - connect_info['access_url'] = access_url + console = self.driver.get_spice_console(context, instance) + connect_info = console.get_connection_info(token, access_url) except exception.InstanceNotFound: if instance['vm_state'] != vm_states.BUILDING: raise @@ -4306,9 +4304,8 @@ class ComputeManager(manager.Manager): try: # Retrieve connect info from driver, and then decorate with our # access info token - connect_info = self.driver.get_rdp_console(context, instance) - connect_info['token'] = token - connect_info['access_url'] = access_url + console = self.driver.get_rdp_console(context, instance) + connect_info = console.get_connection_info(token, access_url) except exception.InstanceNotFound: if instance['vm_state'] != vm_states.BUILDING: raise @@ -4330,7 +4327,7 @@ class ComputeManager(manager.Manager): else: console_info = self.driver.get_vnc_console(ctxt, instance) - return console_info['port'] == port + return console_info.port == port @object_compat @wrap_exception() diff --git a/nova/console/type.py b/nova/console/type.py new file mode 100644 index 000000000000..6958506ddcde --- /dev/null +++ b/nova/console/type.py @@ -0,0 +1,42 @@ +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +class Console(object): + def __init__(self, host, port, internal_access_path=None): + self.host = host + self.port = port + self.internal_access_path = internal_access_path + + def get_connection_info(self, token, access_url): + """Returns an unreferenced dict with connection information.""" + + ret = dict(self.__dict__) + ret['token'] = token + ret['access_url'] = access_url + return ret + + +class ConsoleVNC(Console): + pass + + +class ConsoleRDP(Console): + pass + + +class ConsoleSpice(Console): + def __init__(self, host, port, tlsPort, internal_access_path=None): + super(ConsoleSpice, self).__init__(host, port, internal_access_path) + self.tlsPort = tlsPort diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 93b6b6af0b44..c88a0a25c34c 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -48,6 +48,7 @@ from nova.compute import task_states from nova.compute import utils as compute_utils from nova.compute import vm_states from nova.conductor import manager as conductor_manager +from nova.console import type as ctype from nova import context from nova import db from nova import exception @@ -3091,13 +3092,13 @@ class ComputeTestCase(BaseTestCase): instance = self._create_fake_instance_obj() def fake_driver_get_console(*args, **kwargs): - return {'host': "fake_host", 'port': "5900", - 'internal_access_path': None} + return ctype.ConsoleVNC(host="fake_host", port=5900) + self.stubs.Set(self.compute.driver, "get_vnc_console", fake_driver_get_console) self.assertTrue(self.compute.validate_console_port( - context=self.context, instance=instance, port="5900", + context=self.context, instance=instance, port=5900, console_type="novnc")) def test_validate_console_port_spice(self): @@ -3106,13 +3107,13 @@ class ComputeTestCase(BaseTestCase): instance = self._create_fake_instance_obj() def fake_driver_get_console(*args, **kwargs): - return {'host': "fake_host", 'port': "5900", - 'internal_access_path': None} + return ctype.ConsoleSpice(host="fake_host", port=5900, tlsPort=88) + self.stubs.Set(self.compute.driver, "get_spice_console", fake_driver_get_console) self.assertTrue(self.compute.validate_console_port( - context=self.context, instance=instance, port="5900", + context=self.context, instance=instance, port=5900, console_type="spice-html5")) def test_validate_console_port_rdp(self): @@ -3120,13 +3121,13 @@ class ComputeTestCase(BaseTestCase): instance = self._create_fake_instance_obj() def fake_driver_get_console(*args, **kwargs): - return {'host': "fake_host", 'port': "5900", - 'internal_access_path': None} + return ctype.ConsoleRDP(host="fake_host", port=5900) + self.stubs.Set(self.compute.driver, "get_rdp_console", fake_driver_get_console) self.assertTrue(self.compute.validate_console_port( - context=self.context, instance=instance, port="5900", + context=self.context, instance=instance, port=5900, console_type="rdp-html5")) def test_validate_console_port_wrong_port(self): @@ -3135,8 +3136,8 @@ class ComputeTestCase(BaseTestCase): instance = self._create_fake_instance_obj() def fake_driver_get_console(*args, **kwargs): - return {'host': "fake_host", 'port': "5900", - 'internal_access_path': None} + return ctype.ConsoleSpice(host="fake_host", port=5900, tlsPort=88) + self.stubs.Set(self.compute.driver, "get_vnc_console", fake_driver_get_console) diff --git a/nova/tests/console/test_type.py b/nova/tests/console/test_type.py new file mode 100644 index 000000000000..69c26b06a9a8 --- /dev/null +++ b/nova/tests/console/test_type.py @@ -0,0 +1,59 @@ +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +from nova.console import type as ctype +from nova import test + + +class TypeTestCase(test.TestCase): + def setUp(self): + super(TypeTestCase, self).setUp() + + def test_console(self): + c = ctype.Console(host='127.0.0.1', port=8945) + + self.assertTrue(hasattr(c, 'host')) + self.assertTrue(hasattr(c, 'port')) + self.assertTrue(hasattr(c, 'internal_access_path')) + + self.assertEqual('127.0.0.1', c.host) + self.assertEqual(8945, c.port) + self.assertIsNone(c.internal_access_path) + + self.assertEqual({ + 'host': '127.0.0.1', + 'port': 8945, + 'internal_access_path': None, + 'token': 'a-token', + 'access_url': 'an-url'}, + c.get_connection_info('a-token', 'an-url')) + + def test_console_vnc(self): + c = ctype.ConsoleVNC(host='127.0.0.1', port=8945) + + self.assertIsInstance(c, ctype.Console) + + def test_console_rdp(self): + c = ctype.ConsoleRDP(host='127.0.0.1', port=8945) + + self.assertIsInstance(c, ctype.Console) + + def test_console_spice(self): + c = ctype.ConsoleSpice(host='127.0.0.1', port=8945, tlsPort=6547) + + self.assertIsInstance(c, ctype.Console) + self.assertEqual(6547, c.tlsPort) + self.assertEqual( + 6547, c.get_connection_info('a-token', 'an-url')['tlsPort']) diff --git a/nova/tests/virt/hyperv/test_hypervapi.py b/nova/tests/virt/hyperv/test_hypervapi.py index ae48cc9f9ae4..f5f3fe3ca218 100644 --- a/nova/tests/virt/hyperv/test_hypervapi.py +++ b/nova/tests/virt/hyperv/test_hypervapi.py @@ -1832,9 +1832,9 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase, connect_info = self._conn.get_rdp_console(self._context, instance) self._mox.VerifyAll() - self.assertEqual(CONF.my_ip, connect_info['host']) - self.assertEqual(fake_port, connect_info['port']) - self.assertEqual(fake_vm_id, connect_info['internal_access_path']) + self.assertEqual(CONF.my_ip, connect_info.host) + self.assertEqual(fake_port, connect_info.port) + self.assertEqual(fake_vm_id, connect_info.internal_access_path) class VolumeOpsTestCase(HyperVAPIBaseTestCase): diff --git a/nova/tests/virt/libvirt/test_driver.py b/nova/tests/virt/libvirt/test_driver.py index c2bcca1d0488..acbcb23095f2 100644 --- a/nova/tests/virt/libvirt/test_driver.py +++ b/nova/tests/virt/libvirt/test_driver.py @@ -8294,7 +8294,7 @@ Active: 8381604 kB self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) vnc_dict = conn.get_vnc_console(self.context, instance) - self.assertEqual(vnc_dict['port'], '5900') + self.assertEqual(vnc_dict.port, '5900') def test_get_vnc_console_unavailable(self): instance = self.create_instance_obj(self.context) @@ -8334,7 +8334,7 @@ Active: 8381604 kB self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) spice_dict = conn.get_spice_console(self.context, instance) - self.assertEqual(spice_dict['port'], '5950') + self.assertEqual(spice_dict.port, '5950') def test_get_spice_console_unavailable(self): instance = self.create_instance_obj(self.context) diff --git a/nova/tests/virt/test_virt_drivers.py b/nova/tests/virt/test_virt_drivers.py index 409ff86fc6d3..d28374a8f0dd 100644 --- a/nova/tests/virt/test_virt_drivers.py +++ b/nova/tests/virt/test_virt_drivers.py @@ -22,6 +22,7 @@ import netaddr import six from nova.compute import manager +from nova.console import type as ctype from nova import exception from nova import objects from nova.openstack.common import importutils @@ -539,27 +540,20 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase): def test_get_vnc_console(self): instance, network_info = self._get_running_instance(obj=True) vnc_console = self.connection.get_vnc_console(self.ctxt, instance) - self.assertIn('internal_access_path', vnc_console) - self.assertIn('host', vnc_console) - self.assertIn('port', vnc_console) + self.assertIsInstance(vnc_console, ctype.ConsoleVNC) @catch_notimplementederror def test_get_spice_console(self): instance_ref, network_info = self._get_running_instance() spice_console = self.connection.get_spice_console(self.ctxt, - instance_ref) - self.assertIn('internal_access_path', spice_console) - self.assertIn('host', spice_console) - self.assertIn('port', spice_console) - self.assertIn('tlsPort', spice_console) + instance_ref) + self.assertIsInstance(spice_console, ctype.ConsoleSpice) @catch_notimplementederror def test_get_rdp_console(self): instance_ref, network_info = self._get_running_instance() rdp_console = self.connection.get_rdp_console(self.ctxt, instance_ref) - self.assertIn('internal_access_path', rdp_console) - self.assertIn('host', rdp_console) - self.assertIn('port', rdp_console) + self.assertIsInstance(rdp_console, ctype.ConsoleRDP) @catch_notimplementederror def test_get_console_pool_info(self): diff --git a/nova/tests/virt/xenapi/test_xenapi.py b/nova/tests/virt/xenapi/test_xenapi.py index a3c3d46352c3..df8afc7ebfff 100644 --- a/nova/tests/virt/xenapi/test_xenapi.py +++ b/nova/tests/virt/xenapi/test_xenapi.py @@ -443,7 +443,7 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): # Note(sulo): We don't care about session id in test # they will always differ so strip that out - actual_path = console['internal_access_path'].split('&')[0] + actual_path = console.internal_access_path.split('&')[0] expected_path = "/console?ref=%s" % str(vm_ref) self.assertEqual(expected_path, actual_path) @@ -460,7 +460,7 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): # Note(sulo): We don't care about session id in test # they will always differ so strip that out - actual_path = console['internal_access_path'].split('&')[0] + actual_path = console.internal_access_path.split('&')[0] expected_path = "/console?ref=%s" % str(rescue_vm) self.assertEqual(expected_path, actual_path) diff --git a/nova/virt/driver.py b/nova/virt/driver.py index f12757ad6ac9..9458e309f776 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -373,6 +373,8 @@ class ComputeDriver(object): :param context: security context :param instance: nova.objects.instance.Instance + + :returns an instance of console.type.ConsoleVNC """ raise NotImplementedError() @@ -381,6 +383,8 @@ class ComputeDriver(object): :param context: security context :param instance: nova.objects.instance.Instance + + :returns an instance of console.type.ConsoleSpice """ raise NotImplementedError() @@ -389,6 +393,8 @@ class ComputeDriver(object): :param context: security context :param instance: nova.objects.instance.Instance + + :returns an instance of console.type.ConsoleRDP """ raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 90549d2e0747..6416d0f5080f 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -29,6 +29,7 @@ from oslo.config import cfg from nova.compute import power_state from nova.compute import task_states +from nova.console import type as ctype from nova import db from nova import exception from nova.i18n import _ @@ -337,20 +338,20 @@ class FakeDriver(driver.ComputeDriver): return 'FAKE CONSOLE OUTPUT\nANOTHER\nLAST LINE' def get_vnc_console(self, context, instance): - return {'internal_access_path': 'FAKE', - 'host': 'fakevncconsole.com', - 'port': 6969} + return ctype.ConsoleVNC(internal_access_path='FAKE', + host='fakevncconsole.com', + port=6969) def get_spice_console(self, context, instance): - return {'internal_access_path': 'FAKE', - 'host': 'fakespiceconsole.com', - 'port': 6969, - 'tlsPort': 6970} + return ctype.ConsoleSpice(internal_access_path='FAKE', + host='fakespiceconsole.com', + port=6969, + tlsPort=6970) def get_rdp_console(self, context, instance): - return {'internal_access_path': 'FAKE', - 'host': 'fakerdpconsole.com', - 'port': 6969} + return ctype.ConsoleRDP(internal_access_path='FAKE', + host='fakerdpconsole.com', + port=6969) def get_console_pool_info(self, console_type): return {'address': '127.0.0.1', diff --git a/nova/virt/hyperv/rdpconsoleops.py b/nova/virt/hyperv/rdpconsoleops.py index f6b8adff9669..1f307a98fa88 100644 --- a/nova/virt/hyperv/rdpconsoleops.py +++ b/nova/virt/hyperv/rdpconsoleops.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from nova.console import type as ctype from nova.openstack.common import log as logging from nova.virt.hyperv import hostops from nova.virt.hyperv import utilsfactory @@ -35,6 +36,5 @@ class RDPConsoleOps(object): LOG.debug("RDP console: %(host)s:%(port)s, %(vm_id)s", {"host": host, "port": port, "vm_id": vm_id}) - return {'host': host, - 'port': port, - 'internal_access_path': vm_id} + return ctype.ConsoleRDP( + host=host, port=port, internal_access_path=vm_id) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 5c0d52e6f26d..7b889b9b7678 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -56,6 +56,7 @@ from nova.compute import task_states from nova.compute import utils as compute_utils from nova.compute import vm_mode from nova.console import serial as serial_console +from nova.console import type as ctype from nova import context as nova_context from nova import exception from nova.i18n import _ @@ -2652,7 +2653,7 @@ class LibvirtDriver(driver.ComputeDriver): port = get_vnc_port_for_instance(instance.name) host = CONF.vncserver_proxyclient_address - return {'host': host, 'port': port, 'internal_access_path': None} + return ctype.ConsoleVNC(host=host, port=port) def get_spice_console(self, context, instance): def get_spice_ports_for_instance(instance_name): @@ -2672,8 +2673,7 @@ class LibvirtDriver(driver.ComputeDriver): ports = get_spice_ports_for_instance(instance['name']) host = CONF.spice.server_proxyclient_address - return {'host': host, 'port': ports[0], - 'tlsPort': ports[1], 'internal_access_path': None} + return ctype.ConsoleSpice(host=host, port=ports[0], tlsPort=ports[1]) @staticmethod def _supports_direct_io(dirpath): diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index c28b3b5a04b6..9b6e3e2875d3 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -30,6 +30,7 @@ from nova import compute from nova.compute import power_state from nova.compute import task_states from nova.compute import vm_states +from nova.console import type as ctype from nova import context as nova_context from nova import exception from nova.i18n import _, _LE @@ -1291,7 +1292,7 @@ class VMwareVMOps(object): """Return connection info for a vnc console using ESX logic.""" vnc_console = self._get_vnc_console_connection(instance) vnc_console['host'] = CONF.vmware.host_ip - return vnc_console + return ctype.ConsoleVNC(**vnc_console) @staticmethod def _get_machine_id_str(network_info): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 284a6663e2ea..b0f563f2db60 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -33,6 +33,7 @@ from nova.compute import power_state from nova.compute import task_states from nova.compute import vm_mode from nova.compute import vm_states +from nova.console import type as ctype from nova import context as nova_context from nova import exception from nova.i18n import _ @@ -1616,8 +1617,10 @@ class VMOps(object): path = "/console?ref=%s&session_id=%s" % (str(vm_ref), session_id) # NOTE: XS5.6sp2+ use http over port 80 for xenapi com - return {'host': CONF.vncserver_proxyclient_address, 'port': 80, - 'internal_access_path': path} + return ctype.ConsoleVNC( + host=CONF.vncserver_proxyclient_address, + port=80, + internal_access_path=path) def _vif_xenstore_data(self, vif): """convert a network info vif to injectable instance data."""