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
This commit is contained in:
Sahid Orentino Ferdjaoui 2014-08-26 11:00:34 +00:00
parent 9cf0c413d4
commit 0c3a47e573
14 changed files with 162 additions and 58 deletions

View File

@ -4233,9 +4233,8 @@ class ComputeManager(manager.Manager):
try: try:
# Retrieve connect info from driver, and then decorate with our # Retrieve connect info from driver, and then decorate with our
# access info token # access info token
connect_info = self.driver.get_vnc_console(context, instance) console = self.driver.get_vnc_console(context, instance)
connect_info['token'] = token connect_info = console.get_connection_info(token, access_url)
connect_info['access_url'] = access_url
except exception.InstanceNotFound: except exception.InstanceNotFound:
if instance['vm_state'] != vm_states.BUILDING: if instance['vm_state'] != vm_states.BUILDING:
raise raise
@ -4270,9 +4269,8 @@ class ComputeManager(manager.Manager):
try: try:
# Retrieve connect info from driver, and then decorate with our # Retrieve connect info from driver, and then decorate with our
# access info token # access info token
connect_info = self.driver.get_spice_console(context, instance) console = self.driver.get_spice_console(context, instance)
connect_info['token'] = token connect_info = console.get_connection_info(token, access_url)
connect_info['access_url'] = access_url
except exception.InstanceNotFound: except exception.InstanceNotFound:
if instance['vm_state'] != vm_states.BUILDING: if instance['vm_state'] != vm_states.BUILDING:
raise raise
@ -4306,9 +4304,8 @@ class ComputeManager(manager.Manager):
try: try:
# Retrieve connect info from driver, and then decorate with our # Retrieve connect info from driver, and then decorate with our
# access info token # access info token
connect_info = self.driver.get_rdp_console(context, instance) console = self.driver.get_rdp_console(context, instance)
connect_info['token'] = token connect_info = console.get_connection_info(token, access_url)
connect_info['access_url'] = access_url
except exception.InstanceNotFound: except exception.InstanceNotFound:
if instance['vm_state'] != vm_states.BUILDING: if instance['vm_state'] != vm_states.BUILDING:
raise raise
@ -4330,7 +4327,7 @@ class ComputeManager(manager.Manager):
else: else:
console_info = self.driver.get_vnc_console(ctxt, instance) console_info = self.driver.get_vnc_console(ctxt, instance)
return console_info['port'] == port return console_info.port == port
@object_compat @object_compat
@wrap_exception() @wrap_exception()

42
nova/console/type.py Normal file
View File

@ -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

View File

@ -48,6 +48,7 @@ from nova.compute import task_states
from nova.compute import utils as compute_utils from nova.compute import utils as compute_utils
from nova.compute import vm_states from nova.compute import vm_states
from nova.conductor import manager as conductor_manager from nova.conductor import manager as conductor_manager
from nova.console import type as ctype
from nova import context from nova import context
from nova import db from nova import db
from nova import exception from nova import exception
@ -3091,13 +3092,13 @@ class ComputeTestCase(BaseTestCase):
instance = self._create_fake_instance_obj() instance = self._create_fake_instance_obj()
def fake_driver_get_console(*args, **kwargs): def fake_driver_get_console(*args, **kwargs):
return {'host': "fake_host", 'port': "5900", return ctype.ConsoleVNC(host="fake_host", port=5900)
'internal_access_path': None}
self.stubs.Set(self.compute.driver, "get_vnc_console", self.stubs.Set(self.compute.driver, "get_vnc_console",
fake_driver_get_console) fake_driver_get_console)
self.assertTrue(self.compute.validate_console_port( self.assertTrue(self.compute.validate_console_port(
context=self.context, instance=instance, port="5900", context=self.context, instance=instance, port=5900,
console_type="novnc")) console_type="novnc"))
def test_validate_console_port_spice(self): def test_validate_console_port_spice(self):
@ -3106,13 +3107,13 @@ class ComputeTestCase(BaseTestCase):
instance = self._create_fake_instance_obj() instance = self._create_fake_instance_obj()
def fake_driver_get_console(*args, **kwargs): def fake_driver_get_console(*args, **kwargs):
return {'host': "fake_host", 'port': "5900", return ctype.ConsoleSpice(host="fake_host", port=5900, tlsPort=88)
'internal_access_path': None}
self.stubs.Set(self.compute.driver, "get_spice_console", self.stubs.Set(self.compute.driver, "get_spice_console",
fake_driver_get_console) fake_driver_get_console)
self.assertTrue(self.compute.validate_console_port( 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")) console_type="spice-html5"))
def test_validate_console_port_rdp(self): def test_validate_console_port_rdp(self):
@ -3120,13 +3121,13 @@ class ComputeTestCase(BaseTestCase):
instance = self._create_fake_instance_obj() instance = self._create_fake_instance_obj()
def fake_driver_get_console(*args, **kwargs): def fake_driver_get_console(*args, **kwargs):
return {'host': "fake_host", 'port': "5900", return ctype.ConsoleRDP(host="fake_host", port=5900)
'internal_access_path': None}
self.stubs.Set(self.compute.driver, "get_rdp_console", self.stubs.Set(self.compute.driver, "get_rdp_console",
fake_driver_get_console) fake_driver_get_console)
self.assertTrue(self.compute.validate_console_port( 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")) console_type="rdp-html5"))
def test_validate_console_port_wrong_port(self): def test_validate_console_port_wrong_port(self):
@ -3135,8 +3136,8 @@ class ComputeTestCase(BaseTestCase):
instance = self._create_fake_instance_obj() instance = self._create_fake_instance_obj()
def fake_driver_get_console(*args, **kwargs): def fake_driver_get_console(*args, **kwargs):
return {'host': "fake_host", 'port': "5900", return ctype.ConsoleSpice(host="fake_host", port=5900, tlsPort=88)
'internal_access_path': None}
self.stubs.Set(self.compute.driver, "get_vnc_console", self.stubs.Set(self.compute.driver, "get_vnc_console",
fake_driver_get_console) fake_driver_get_console)

View File

@ -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'])

View File

@ -1832,9 +1832,9 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase,
connect_info = self._conn.get_rdp_console(self._context, instance) connect_info = self._conn.get_rdp_console(self._context, instance)
self._mox.VerifyAll() self._mox.VerifyAll()
self.assertEqual(CONF.my_ip, connect_info['host']) self.assertEqual(CONF.my_ip, connect_info.host)
self.assertEqual(fake_port, connect_info['port']) self.assertEqual(fake_port, connect_info.port)
self.assertEqual(fake_vm_id, connect_info['internal_access_path']) self.assertEqual(fake_vm_id, connect_info.internal_access_path)
class VolumeOpsTestCase(HyperVAPIBaseTestCase): class VolumeOpsTestCase(HyperVAPIBaseTestCase):

View File

@ -8294,7 +8294,7 @@ Active: 8381604 kB
self.mox.ReplayAll() self.mox.ReplayAll()
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
vnc_dict = conn.get_vnc_console(self.context, instance) 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): def test_get_vnc_console_unavailable(self):
instance = self.create_instance_obj(self.context) instance = self.create_instance_obj(self.context)
@ -8334,7 +8334,7 @@ Active: 8381604 kB
self.mox.ReplayAll() self.mox.ReplayAll()
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
spice_dict = conn.get_spice_console(self.context, instance) 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): def test_get_spice_console_unavailable(self):
instance = self.create_instance_obj(self.context) instance = self.create_instance_obj(self.context)

View File

@ -22,6 +22,7 @@ import netaddr
import six import six
from nova.compute import manager from nova.compute import manager
from nova.console import type as ctype
from nova import exception from nova import exception
from nova import objects from nova import objects
from nova.openstack.common import importutils from nova.openstack.common import importutils
@ -539,27 +540,20 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase):
def test_get_vnc_console(self): def test_get_vnc_console(self):
instance, network_info = self._get_running_instance(obj=True) instance, network_info = self._get_running_instance(obj=True)
vnc_console = self.connection.get_vnc_console(self.ctxt, instance) vnc_console = self.connection.get_vnc_console(self.ctxt, instance)
self.assertIn('internal_access_path', vnc_console) self.assertIsInstance(vnc_console, ctype.ConsoleVNC)
self.assertIn('host', vnc_console)
self.assertIn('port', vnc_console)
@catch_notimplementederror @catch_notimplementederror
def test_get_spice_console(self): def test_get_spice_console(self):
instance_ref, network_info = self._get_running_instance() instance_ref, network_info = self._get_running_instance()
spice_console = self.connection.get_spice_console(self.ctxt, spice_console = self.connection.get_spice_console(self.ctxt,
instance_ref) instance_ref)
self.assertIn('internal_access_path', spice_console) self.assertIsInstance(spice_console, ctype.ConsoleSpice)
self.assertIn('host', spice_console)
self.assertIn('port', spice_console)
self.assertIn('tlsPort', spice_console)
@catch_notimplementederror @catch_notimplementederror
def test_get_rdp_console(self): def test_get_rdp_console(self):
instance_ref, network_info = self._get_running_instance() instance_ref, network_info = self._get_running_instance()
rdp_console = self.connection.get_rdp_console(self.ctxt, instance_ref) rdp_console = self.connection.get_rdp_console(self.ctxt, instance_ref)
self.assertIn('internal_access_path', rdp_console) self.assertIsInstance(rdp_console, ctype.ConsoleRDP)
self.assertIn('host', rdp_console)
self.assertIn('port', rdp_console)
@catch_notimplementederror @catch_notimplementederror
def test_get_console_pool_info(self): def test_get_console_pool_info(self):

View File

@ -443,7 +443,7 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
# Note(sulo): We don't care about session id in test # Note(sulo): We don't care about session id in test
# they will always differ so strip that out # 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) expected_path = "/console?ref=%s" % str(vm_ref)
self.assertEqual(expected_path, actual_path) 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 # Note(sulo): We don't care about session id in test
# they will always differ so strip that out # 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) expected_path = "/console?ref=%s" % str(rescue_vm)
self.assertEqual(expected_path, actual_path) self.assertEqual(expected_path, actual_path)

View File

@ -373,6 +373,8 @@ class ComputeDriver(object):
:param context: security context :param context: security context
:param instance: nova.objects.instance.Instance :param instance: nova.objects.instance.Instance
:returns an instance of console.type.ConsoleVNC
""" """
raise NotImplementedError() raise NotImplementedError()
@ -381,6 +383,8 @@ class ComputeDriver(object):
:param context: security context :param context: security context
:param instance: nova.objects.instance.Instance :param instance: nova.objects.instance.Instance
:returns an instance of console.type.ConsoleSpice
""" """
raise NotImplementedError() raise NotImplementedError()
@ -389,6 +393,8 @@ class ComputeDriver(object):
:param context: security context :param context: security context
:param instance: nova.objects.instance.Instance :param instance: nova.objects.instance.Instance
:returns an instance of console.type.ConsoleRDP
""" """
raise NotImplementedError() raise NotImplementedError()

View File

@ -29,6 +29,7 @@ from oslo.config import cfg
from nova.compute import power_state from nova.compute import power_state
from nova.compute import task_states from nova.compute import task_states
from nova.console import type as ctype
from nova import db from nova import db
from nova import exception from nova import exception
from nova.i18n import _ from nova.i18n import _
@ -337,20 +338,20 @@ class FakeDriver(driver.ComputeDriver):
return 'FAKE CONSOLE OUTPUT\nANOTHER\nLAST LINE' return 'FAKE CONSOLE OUTPUT\nANOTHER\nLAST LINE'
def get_vnc_console(self, context, instance): def get_vnc_console(self, context, instance):
return {'internal_access_path': 'FAKE', return ctype.ConsoleVNC(internal_access_path='FAKE',
'host': 'fakevncconsole.com', host='fakevncconsole.com',
'port': 6969} port=6969)
def get_spice_console(self, context, instance): def get_spice_console(self, context, instance):
return {'internal_access_path': 'FAKE', return ctype.ConsoleSpice(internal_access_path='FAKE',
'host': 'fakespiceconsole.com', host='fakespiceconsole.com',
'port': 6969, port=6969,
'tlsPort': 6970} tlsPort=6970)
def get_rdp_console(self, context, instance): def get_rdp_console(self, context, instance):
return {'internal_access_path': 'FAKE', return ctype.ConsoleRDP(internal_access_path='FAKE',
'host': 'fakerdpconsole.com', host='fakerdpconsole.com',
'port': 6969} port=6969)
def get_console_pool_info(self, console_type): def get_console_pool_info(self, console_type):
return {'address': '127.0.0.1', return {'address': '127.0.0.1',

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from nova.console import type as ctype
from nova.openstack.common import log as logging from nova.openstack.common import log as logging
from nova.virt.hyperv import hostops from nova.virt.hyperv import hostops
from nova.virt.hyperv import utilsfactory from nova.virt.hyperv import utilsfactory
@ -35,6 +36,5 @@ class RDPConsoleOps(object):
LOG.debug("RDP console: %(host)s:%(port)s, %(vm_id)s", LOG.debug("RDP console: %(host)s:%(port)s, %(vm_id)s",
{"host": host, "port": port, "vm_id": vm_id}) {"host": host, "port": port, "vm_id": vm_id})
return {'host': host, return ctype.ConsoleRDP(
'port': port, host=host, port=port, internal_access_path=vm_id)
'internal_access_path': vm_id}

View File

@ -56,6 +56,7 @@ from nova.compute import task_states
from nova.compute import utils as compute_utils from nova.compute import utils as compute_utils
from nova.compute import vm_mode from nova.compute import vm_mode
from nova.console import serial as serial_console 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 context as nova_context
from nova import exception from nova import exception
from nova.i18n import _ from nova.i18n import _
@ -2652,7 +2653,7 @@ class LibvirtDriver(driver.ComputeDriver):
port = get_vnc_port_for_instance(instance.name) port = get_vnc_port_for_instance(instance.name)
host = CONF.vncserver_proxyclient_address 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_console(self, context, instance):
def get_spice_ports_for_instance(instance_name): def get_spice_ports_for_instance(instance_name):
@ -2672,8 +2673,7 @@ class LibvirtDriver(driver.ComputeDriver):
ports = get_spice_ports_for_instance(instance['name']) ports = get_spice_ports_for_instance(instance['name'])
host = CONF.spice.server_proxyclient_address host = CONF.spice.server_proxyclient_address
return {'host': host, 'port': ports[0], return ctype.ConsoleSpice(host=host, port=ports[0], tlsPort=ports[1])
'tlsPort': ports[1], 'internal_access_path': None}
@staticmethod @staticmethod
def _supports_direct_io(dirpath): def _supports_direct_io(dirpath):

View File

@ -30,6 +30,7 @@ from nova import compute
from nova.compute import power_state from nova.compute import power_state
from nova.compute import task_states from nova.compute import task_states
from nova.compute import vm_states from nova.compute import vm_states
from nova.console import type as ctype
from nova import context as nova_context from nova import context as nova_context
from nova import exception from nova import exception
from nova.i18n import _, _LE from nova.i18n import _, _LE
@ -1291,7 +1292,7 @@ class VMwareVMOps(object):
"""Return connection info for a vnc console using ESX logic.""" """Return connection info for a vnc console using ESX logic."""
vnc_console = self._get_vnc_console_connection(instance) vnc_console = self._get_vnc_console_connection(instance)
vnc_console['host'] = CONF.vmware.host_ip vnc_console['host'] = CONF.vmware.host_ip
return vnc_console return ctype.ConsoleVNC(**vnc_console)
@staticmethod @staticmethod
def _get_machine_id_str(network_info): def _get_machine_id_str(network_info):

View File

@ -33,6 +33,7 @@ from nova.compute import power_state
from nova.compute import task_states from nova.compute import task_states
from nova.compute import vm_mode from nova.compute import vm_mode
from nova.compute import vm_states from nova.compute import vm_states
from nova.console import type as ctype
from nova import context as nova_context from nova import context as nova_context
from nova import exception from nova import exception
from nova.i18n import _ from nova.i18n import _
@ -1616,8 +1617,10 @@ class VMOps(object):
path = "/console?ref=%s&session_id=%s" % (str(vm_ref), session_id) path = "/console?ref=%s&session_id=%s" % (str(vm_ref), session_id)
# NOTE: XS5.6sp2+ use http over port 80 for xenapi com # NOTE: XS5.6sp2+ use http over port 80 for xenapi com
return {'host': CONF.vncserver_proxyclient_address, 'port': 80, return ctype.ConsoleVNC(
'internal_access_path': path} host=CONF.vncserver_proxyclient_address,
port=80,
internal_access_path=path)
def _vif_xenstore_data(self, vif): def _vif_xenstore_data(self, vif):
"""convert a network info vif to injectable instance data.""" """convert a network info vif to injectable instance data."""